diff --git a/.gitignore b/.gitignore index d21d4a6..ea01dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,6 @@ mli-versions.xml *.pro *.pro.user build-* -src/CaseCreator/* *.0cf0304 *.save-failed osx/AppFileStaging/Contents/Resources/Cases/ diff --git a/src/CaseCreator/CaseContent/Animation.cpp b/src/CaseCreator/CaseContent/Animation.cpp new file mode 100644 index 0000000..f909db5 --- /dev/null +++ b/src/CaseCreator/CaseContent/Animation.cpp @@ -0,0 +1,379 @@ +#include "Animation.h" + +#include "XmlReader.h" +#include "XmlWriter.h" + +#include "Staging/CaseContentLoadingStager.h" + +Animation::Animation(Staging::Animation *pStagingAnimation) +{ + for (Staging::Animation::Frame *pFrame : pStagingAnimation->FrameList) + { + frameList.push_back(new Animation::Frame(pFrame)); + } +} + +Animation::~Animation() +{ + for (Animation::Frame *pFrame : frameList) + { + delete pFrame; + } + + frameList.clear(); +} + +void Animation::AddFrame(int msDuration, const QString &spriteFilePath) +{ + frameList.push_back(new Animation::Frame(msDuration, spriteFilePath)); +} + +void Animation::SetFrame(int index, QString filePath) +{ + frameList[index]->SetSpriteFilePath(filePath); +} + +void Animation::SetFrames(QStringList filePaths) +{ + for (Animation::Frame *pFrame : frameList) + { + delete pFrame; + } + + frameList.clear(); + + for (QString filePath : filePaths) + { + frameList.push_back(new Animation::Frame(30, filePath)); + } +} + +void Animation::InsertFrame(int index, QString filePath) +{ + frameList.insert(index, new Animation::Frame(30, filePath)); +} + +void Animation::RemoveFrame(int index) +{ + Animation::Frame *pRemovedFrame = frameList[index]; + frameList.removeAt(index); + delete pRemovedFrame; +} + +QStringList Animation::GetFramePaths() +{ + QStringList framePaths; + + for (Animation::Frame *pFrame : frameList) + { + framePaths.append(pFrame->GetSpriteFilePath()); + } + + return framePaths; +} + +Animation::Frame::Frame(Staging::Animation::Frame *pStagingAnimationFrame) +{ + msDuration = pStagingAnimationFrame->MsDuration; + spriteFilePath = CaseContentLoadingStager::GetCurrent()->GetSpriteFilePathFromId(pStagingAnimationFrame->SpriteId); +} + +Animation::Frame::~Frame() +{ + +} + +Animation::Frame::DrawingView::DrawingView(Animation::Frame *pFrame, IDrawable *pParent) + : IDrawable(pParent) + , IDrawingView(pFrame, pParent) +{ + spritePixmap = QPixmap(pObject->spriteFilePath); +} + +Animation::Frame::DrawingView::~DrawingView() +{ + spritePixmap = QPixmap(); +} + +Animation::DrawingView::DrawingView(Animation *pAnimation, IDrawable *pParent) + : IDrawable(pParent) + , IDrawingView(pAnimation, pParent) +{ + for (Animation::Frame *pFrame : pObject->frameList) + { + frameList.push_back(pFrame->GetDrawingView(this)); + } + + currentIndex = 0; + msTimeoutDuration = frameList[currentIndex]->GetTimeoutDuration(); + positionIsSet = false; + opacity = 1.0; + flipHorizontalIsSet = false; + flipHorizontal = false; +} + +Animation::DrawingView::~DrawingView() +{ + for (Animation::Frame::DrawingView *pFrame : frameList) + { + delete pFrame; + } + + frameList.clear(); +} + +void Animation::DrawingView::DrawCore(QGraphicsScene *pScene, QList &addedItems) +{ + frameGraphicsItemList.clear(); + + for (Animation::Frame::DrawingView *pFrame : frameList) + { + QGraphicsItem *pFrameGraphicsItem = pScene->addPixmap(pFrame->GetSpritePixmap()); + + frameGraphicsItemList.push_back(pFrameGraphicsItem); + addedItems.push_back(pFrameGraphicsItem); + } +} + +void Animation::DrawingView::UpdateCore() +{ + // The way animations work is that we add graphics items for every frame + // and just hide all of the frames that aren't the current frame. + // This makes it so we don't need to keep adding and removing graphics items + // from the scene every time we need to move to a new frame. + for (int i = 0; i < min(frameList.count(), frameGraphicsItemList.count()); i++) + { + if (i != currentIndex) + { + frameGraphicsItemList[i]->setOpacity(0.0); + } + } +} + +void Animation::DrawingView::UpdateOnTimeout() +{ + currentIndex = (currentIndex + 1) % frameList.count(); + msTimeoutDuration = frameList[currentIndex]->GetTimeoutDuration(); +} + +Vector2 Animation::DrawingView::GetPosition() +{ + if (positionIsSet) + { + return position; + } + else + { + return IDrawingView::GetPosition(); + } +} + +void Animation::DrawingView::SetPosition(Vector2 position) +{ + positionIsSet = true; + this->position = position; +} + +qreal Animation::DrawingView::GetOpacity() +{ + qreal netOpacity = opacity; + + if (pParent != NULL) + { + netOpacity *= pParent->GetOpacity(); + } + + return netOpacity; +} + +void Animation::DrawingView::SetOpacity(qreal opacity) +{ + this->opacity = opacity; + Update(); +} + +bool Animation::DrawingView::GetFlipHorizontal() +{ + if (flipHorizontalIsSet) + { + return flipHorizontal; + } + else + { + return IDrawingView::GetFlipHorizontal(); + } +} + +void Animation::DrawingView::SetFlipHorizontal(bool flipHorizontal) +{ + flipHorizontalIsSet = true; + this->flipHorizontal = flipHorizontal; +} + +Animation::CompositeDrawingView::CompositeDrawingView(IDrawable *pParent) + : IDrawable(pParent) +{ + currentIndex = 0; + msTimeoutDuration = 0; + positionIsSet = false; + opacity = 1.0; + + flipHorizontalByAnimationIndexMap.clear(); + animationStartIndexes.clear(); +} + +Animation::CompositeDrawingView::~CompositeDrawingView() +{ + for (Animation::Frame::DrawingView *pFrame : frameList) + { + delete pFrame; + } + + frameList.clear(); +} + +void Animation::CompositeDrawingView::AddAnimation(Animation *pAnimation, bool flipHorizontal) +{ + flipHorizontalByAnimationIndexMap.insert(animationStartIndexes.size(), flipHorizontal); + animationStartIndexes.append(frameList.size()); + + for (Animation::Frame *pFrame : pAnimation->frameList) + { + Animation::Frame::DrawingView *pFrameDrawingView = pFrame->GetDrawingView(this); + frameList.push_back(pFrameDrawingView); + } + + if (frameList.size() > 0 && msTimeoutDuration == 0) + { + msTimeoutDuration = frameList[0]->GetTimeoutDuration(); + } +} + +void Animation::CompositeDrawingView::ReplaceAnimation(int animationIndex, Animation *pAnimation, bool flipHorizontal) +{ + if (animationIndex >= animationStartIndexes.size()) + { + throw new MLIException("Trying to replace an animation that doesn't exist."); + } + + int startIndex = animationStartIndexes[animationIndex]; + int endIndex = animationIndex + 1 == animationStartIndexes.size() ? frameList.size() : animationStartIndexes[animationIndex + 1]; + + for (int i = endIndex - 1; i >= startIndex; i--) + { + Animation::Frame::DrawingView *pFrameDrawingView = frameList[i]; + frameList.removeAt(i); + delete pFrameDrawingView; + } + + flipHorizontalByAnimationIndexMap.remove(animationIndex); + + int currentIndex = 0; + + for (Animation::Frame *pFrame : pAnimation->frameList) + { + Animation::Frame::DrawingView *pFrameDrawingView = pFrame->GetDrawingView(this); + frameList.push_back(pFrameDrawingView); + + currentIndex++; + } + + flipHorizontalByAnimationIndexMap.insert(animationIndex, flipHorizontal); + + int indexDelta = currentIndex - (endIndex - startIndex); + + for (int i = animationIndex + 1; i < animationStartIndexes.size(); i++) + { + animationStartIndexes[i] += indexDelta; + } + + if (frameList.size() > 0 && msTimeoutDuration == 0) + { + msTimeoutDuration = frameList[0]->GetTimeoutDuration(); + } + + Redraw(); +} + +void Animation::CompositeDrawingView::DrawCore(QGraphicsScene *pScene, QList &addedItems) +{ + frameGraphicsItemList.clear(); + + for (Animation::Frame::DrawingView *pFrame : frameList) + { + QGraphicsItem *pFrameGraphicsItem = pScene->addPixmap(pFrame->GetSpritePixmap()); + + frameGraphicsItemList.push_back(pFrameGraphicsItem); + addedItems.push_back(pFrameGraphicsItem); + } +} + +void Animation::CompositeDrawingView::UpdateCore() +{ + // The way animations work is that we add graphics items for every frame + // and just hide all of the frames that aren't the current frame. + // This makes it so we don't need to keep adding and removing graphics items + // from the scene every time we need to move to a new frame. + for (int i = 0; i < min(frameList.count(), frameGraphicsItemList.count()); i++) + { + if (i != currentIndex) + { + frameGraphicsItemList[i]->setOpacity(0.0); + } + } +} + +void Animation::CompositeDrawingView::UpdateOnTimeout() +{ + currentIndex = (currentIndex + 1) % frameList.count(); + msTimeoutDuration = frameList[currentIndex]->GetTimeoutDuration(); +} + +Vector2 Animation::CompositeDrawingView::GetPosition() +{ + if (positionIsSet) + { + return position; + } + else + { + return IDrawable::GetPosition(); + } +} + +void Animation::CompositeDrawingView::SetPosition(Vector2 position) +{ + positionIsSet = true; + this->position = position; +} + +qreal Animation::CompositeDrawingView::GetOpacity() +{ + qreal netOpacity = opacity; + + if (pParent != NULL) + { + netOpacity *= pParent->GetOpacity(); + } + + return netOpacity; +} + +void Animation::CompositeDrawingView::SetOpacity(qreal opacity) +{ + this->opacity = opacity; + Update(); +} + +bool Animation::CompositeDrawingView::GetFlipHorizontal() +{ + int animationIndex = 0; + + while (animationIndex + 1 < animationStartIndexes.size() && + currentIndex >= animationStartIndexes[animationIndex + 1]) + { + animationIndex++; + } + + return flipHorizontalByAnimationIndexMap[animationIndex]; +} diff --git a/src/CaseCreator/CaseContent/Animation.h b/src/CaseCreator/CaseContent/Animation.h new file mode 100644 index 0000000..c1149fc --- /dev/null +++ b/src/CaseCreator/CaseContent/Animation.h @@ -0,0 +1,196 @@ +#ifndef ANIMATION_H +#define ANIMATION_H + +#include "CaseCreator/Utilities/Interfaces.h" +#include "XmlStorableObject.h" + +#include "Staging/Animation.Staging.h" + +#include +#include +#include +#include + +class XmlReader; +class XmlWriter; + +class Animation : public XmlStorableObject +{ + BEGIN_XML_STORABLE_OBJECT(Animation) + XML_STORABLE_CUSTOM_OBJECT_LIST(frameList, Animation::Frame::CreateFromXml) + END_XML_STORABLE_OBJECT() + +public: + Animation() { } + Animation(Staging::Animation *pStagingAnimation); + virtual ~Animation(); + + static Animation * CreateFromXml(XmlReader *pReader) + { + return new Animation(pReader); + } + + void AddFrame(int msDuration, const QString &spriteFilePath); + void SetFrame(int index, QString filePath); + void SetFrames(QStringList filePaths); + void InsertFrame(int index, QString filePath); + void RemoveFrame(int index); + + QStringList GetFramePaths(); + + class Frame : public XmlStorableObject + { + friend class Animation; + + BEGIN_XML_STORABLE_OBJECT(Frame) + XML_STORABLE_INT(msDuration) + XML_STORABLE_FILE_PATH(spriteFilePath) + END_XML_STORABLE_OBJECT() + + public: + Frame() { } + + Frame(int msDuration, const QString &spriteFilePath) + { + this->msDuration = msDuration; + this->spriteFilePath = spriteFilePath; + } + + Frame(Staging::Animation::Frame *pStagingAnimationFrame); + virtual ~Frame(); + + static Frame * CreateFromXml(XmlReader *pReader) + { + return new Frame(pReader); + } + + bool GetIsForever() const { return msDuration == 0; } + + QString GetSpriteFilePath() { return spriteFilePath; } + void SetSpriteFilePath(const QString &filePath) { spriteFilePath = filePath; } + + class DrawingView : public IDrawingView + { + friend class Animation::Frame; + + public: + DrawingView(Animation::Frame *pFrame, IDrawable *pParent); + virtual ~DrawingView(); + + void DrawCore(QGraphicsScene *, QList &) + { + // We should not be drawing anything in the frame - + // instead, the animation should be handling that. + throw new MLIException("Animation::Frame::DrawingView::Draw() should never be called. Instead, GetSpritePixmap() should be called in Animation::DrawingView::Draw()."); + } + + QPixmap & GetSpritePixmap() { return spritePixmap; } + int GetTimeoutDuration() { return pObject->msDuration; } + + private: + QPixmap spritePixmap; + }; + + GET_DRAWING_VIEW_DEFINITION(Animation::Frame) + + private: + int msDuration; + QString spriteFilePath; + }; + + class DrawingView : public IDrawingView + { + friend class Animation; + + public: + DrawingView(Animation *pAnimation, IDrawable *pParent); + ~DrawingView(); + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void UpdateCore(); + void UpdateOnTimeout(); + + Vector2 GetPosition() override; + void SetPosition(Vector2 position); + + qreal GetOpacity() override; + void SetOpacity(qreal opacity); + + bool GetFlipHorizontal() override; + void SetFlipHorizontal(bool flipHorizontal); + + RectangleWH GetBoundingRect() + { + return + (frameList.empty() || + frameList[0]->GetSpritePixmap().width() <= 0 || + frameList[0]->GetSpritePixmap().height() <= 0) ? + IDrawable::GetBoundingRect() : + RectangleWH(0, 0, frameList[0]->GetSpritePixmap().width(), frameList[0]->GetSpritePixmap().height()); + } + + private: + QList frameList; + QList frameGraphicsItemList; + + int currentIndex; + bool positionIsSet; + Vector2 position; + qreal opacity; + bool flipHorizontalIsSet; + bool flipHorizontal; + }; + + GET_DRAWING_VIEW_DEFINITION(Animation) + + class CompositeDrawingView : public IDrawable + { + friend class Animation; + + public: + CompositeDrawingView(IDrawable *pParent); + ~CompositeDrawingView(); + + void AddAnimation(Animation *pAnimation, bool flipHorizontal); + void ReplaceAnimation(int animationIndex, Animation *pAnimation, bool flipHorizontal); + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void UpdateCore(); + void UpdateOnTimeout(); + + Vector2 GetPosition() override; + void SetPosition(Vector2 position); + + qreal GetOpacity() override; + void SetOpacity(qreal opacity); + + bool GetFlipHorizontal() override; + + RectangleWH GetBoundingRect() + { + return + (frameList.empty() || + frameList[0]->GetSpritePixmap().width() <= 0 || + frameList[0]->GetSpritePixmap().height() <= 0) ? + IDrawable::GetBoundingRect() : + RectangleWH(0, 0, frameList[0]->GetSpritePixmap().width(), frameList[0]->GetSpritePixmap().height()); + } + + private: + QList frameList; + QList frameGraphicsItemList; + + int currentIndex; + bool positionIsSet; + Vector2 position; + qreal opacity; + + QMap flipHorizontalByAnimationIndexMap; + QList animationStartIndexes; + }; + +private: + QList frameList; +}; + +#endif // ANIMATION_H diff --git a/src/CaseCreator/CaseContent/BackgroundMusic.cpp b/src/CaseCreator/CaseContent/BackgroundMusic.cpp new file mode 100644 index 0000000..60a56e3 --- /dev/null +++ b/src/CaseCreator/CaseContent/BackgroundMusic.cpp @@ -0,0 +1,35 @@ +#include "BackgroundMusic.h" + +#include "CaseContent.h" + +BackgroundMusicSelector::BackgroundMusicSelector(QWidget *parent) + : QComboBox(parent) +{ + bgmIds = CaseContent::GetInstance()->GetIds(); + previousIndex = -1; + + addItems(bgmIds); + + QObject::connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(CurrentIndexChanged(int))); +} + +QString BackgroundMusicSelector::GetId() +{ + return currentText(); +} + +void BackgroundMusicSelector::SetToId(const QString &id) +{ + int indexOfCurrentEvidence = bgmIds.indexOf(QRegExp(id, Qt::CaseInsensitive)); + + if (indexOfCurrentEvidence >= 0) + { + setCurrentIndex(indexOfCurrentEvidence); + } +} + +void BackgroundMusicSelector::CurrentIndexChanged(int currentIndex) +{ + previousIndex = currentIndex; + emit BackgroundMusicSelected(bgmIds[currentIndex]); +} diff --git a/src/CaseCreator/CaseContent/BackgroundMusic.h b/src/CaseCreator/CaseContent/BackgroundMusic.h new file mode 100644 index 0000000..a88cae8 --- /dev/null +++ b/src/CaseCreator/CaseContent/BackgroundMusic.h @@ -0,0 +1,59 @@ +#ifndef BACKGROUNDMUSIC_H +#define BACKGROUNDMUSIC_H + +#include "XmlStorableObject.h" + +#include +#include + +class XmlReader; +class XmlWriter; + +class BackgroundMusic : public XmlStorableObject +{ + BEGIN_XML_STORABLE_OBJECT(BackgroundMusic) + XML_STORABLE_TEXT(id) + XML_STORABLE_TEXT(filePath) + END_XML_STORABLE_OBJECT() + +public: + BackgroundMusic() { } + + BackgroundMusic(const QString &id, const QString &filePath) + { + this->id = id; + this->filePath = filePath; + } + + virtual ~BackgroundMusic() { } + + QString GetId() { return id; } + QString GetDisplayName() { return id; } + +private: + QString id; + QString filePath; +}; + +class BackgroundMusicSelector : public QComboBox +{ + Q_OBJECT + +public: + explicit BackgroundMusicSelector(QWidget *parent = 0); + + QString GetId(); + void SetToId(const QString &id); + +signals: + void BackgroundMusicSelected(const QString &bgmId); + +public slots: + void CurrentIndexChanged(int currentIndex); + +private: + QStringList bgmIds; + int previousIndex; +}; + +#endif // BACKGROUNDMUSIC_H diff --git a/src/CaseCreator/CaseContent/CaseContent.cpp b/src/CaseCreator/CaseContent/CaseContent.cpp new file mode 100644 index 0000000..5a1f79f --- /dev/null +++ b/src/CaseCreator/CaseContent/CaseContent.cpp @@ -0,0 +1,394 @@ +#include "CaseContent.h" + +#include "Staging/CaseContentLoadingStager.h" +#include "CaseCreator/Utilities/ArchiveWriter.h" +#include "CaseCreator/Utilities/Utilities.h" + +#include "Staging/ForegroundElement.Staging.h" + +#define DECLARE_CASE_CONTENT_ADD_FROM_STAGER_CALL(TYPE) \ + pInstance->Add##TYPE##sFromStager(); + +#define DECLARE_CASE_CONTENT_ADD_FROM_PROJECT_FILE_CALL(TYPE) \ + pInstance->Add##TYPE##sFromProjectFile(&projectFileReader); + +#define DECLARE_CASE_CONTENT_SAVE_TO_PROJECT_FILE_CALL(TYPE) \ + pInstance->Save##TYPE##sToProjectFile(&projectFileWriter); + +#define DECLARE_CASE_CONTENT_MAP_OPERATION_FUNCTIONS(TYPE) \ +template <> \ +void CaseContent::Add(QString id, TYPE *pObject, void *) \ +{ \ + CASE_CONTENT_MAP_NAME(TYPE)[id] = pObject; \ +} \ +\ +template <> \ +TYPE * CaseContent::GetById(QString id, void *) \ +{ \ + if (CASE_CONTENT_MAP_NAME(TYPE).contains(id)) \ + { \ + return CASE_CONTENT_MAP_NAME(TYPE)[id]; \ + } \ + else \ + { \ + return NULL; \ + } \ +} \ +\ +template <> \ +QStringList CaseContent::GetIds(void *) \ +{ \ + return CASE_CONTENT_MAP_NAME(TYPE).keys(); \ +} \ +\ +template <> \ +QStringList CaseContent::GetDisplayNames(void *) \ +{ \ + QStringList displayNameList; \ + \ + for (QMap::iterator iter = CASE_CONTENT_MAP_NAME(TYPE).begin(); iter != CASE_CONTENT_MAP_NAME(TYPE).end(); iter++) \ + { \ + displayNameList.push_back(iter.value()->GetDisplayName()); \ + } \ + \ + return displayNameList; \ +} + +#define DECLARE_CASE_CONTENT_ADD_FROM_PROJECT_FILE_FUNCTION(TYPE) \ +void CaseContent::Add##TYPE##sFromProjectFile(XmlReader *pReader) \ +{ \ + if (pReader->ElementExists(#TYPE "s")) \ + { \ + pReader->StartElement(#TYPE "s"); \ + pReader->StartList(#TYPE); \ + \ + while (pReader->MoveToNextListItem()) \ + { \ + TYPE *pObject = new TYPE(pReader); \ + Add(pObject->GetId(), pObject); \ + } \ + \ + pReader->EndElement(); \ + } \ +} + +#define DECLARE_CASE_CONTENT_SAVE_TO_PROJECT_FILE_FUNCTION(TYPE) \ +void CaseContent::Save##TYPE##sToProjectFile(XmlWriter *pWriter) \ +{ \ + pWriter->StartElement(#TYPE "s"); \ + \ + for (QMap::iterator iter = CASE_CONTENT_MAP_NAME(TYPE).begin(); iter != CASE_CONTENT_MAP_NAME(TYPE).end(); iter++) \ + { \ + pWriter->StartElement(#TYPE); \ + (*iter)->SaveToXml(pWriter); \ + pWriter->EndElement(); \ + } \ + \ + pWriter->EndElement(); \ +} + +CaseContent *CaseContent::pInstance = NULL; + +CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_MAP_OPERATION_FUNCTIONS) +CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_ADD_FROM_PROJECT_FILE_FUNCTION) +CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_SAVE_TO_PROJECT_FILE_FUNCTION) + +void CaseContent::ReplaceInstanceWithStager() +{ + delete pInstance; + pInstance = new CaseContent(); + + pInstance->projectFileDir = CaseContentLoadingStager::GetCurrent()->ProjectFileDir; + pInstance->projectFilePath = CaseContentLoadingStager::GetCurrent()->ProjectFilePath; + + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_ADD_FROM_STAGER_CALL) +} + +void CaseContent::ReplaceInstanceFromProjectFile(QString filePath) +{ + delete pInstance; + pInstance = new CaseContent(); + + pInstance->projectFileDir = filePath.left(filePath.lastIndexOf('/')); + pInstance->projectFilePath = filePath; + + XmlReader projectFileReader(filePath.toStdString().c_str()); + + projectFileReader.StartElement("CaseContent"); + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_ADD_FROM_PROJECT_FILE_CALL) + projectFileReader.EndElement(); +} + +void CaseContent::SaveToProjectFile() +{ + XmlWriter projectFileWriter(projectFilePath.toStdString().c_str(), NULL, true /* makeHumanReadable */, 2 /* formattingVersion */); + + projectFileWriter.StartElement("CaseContent"); + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_SAVE_TO_PROJECT_FILE_CALL) + projectFileWriter.EndElement(); +} + +void CaseContent::CompileToCaseFile(QString filePath) +{ + ArchiveWriter caseArchiveWriter; + + if (!caseArchiveWriter.Init(projectFileDir.absolutePath() + "/" + filePath)) + { + return; + } + + int spriteIndex = 0; + + for (QMap::iterator iter = LocationIdToLocationMap.begin(); iter != LocationIdToLocationMap.end(); iter++) + { + char filename[256]; + sprintf(filename, "Images/Background%d.png", spriteIndex + 1); + spriteIndex++; + + if (!caseArchiveWriter.SaveFile(iter.value()->GetBackgroundFilePath(), filename)) + { + return; + } + } + + XmlWriter caseFileWriter(NULL, NULL, true /* makeHumanReadable */, 2 /* formattingVersion */); + caseFileWriter.StartElement("Case"); + + WriteAnimationManager(&caseFileWriter); + WriteAudioManager(&caseFileWriter); + WriteContent(&caseFileWriter); + WriteDialogCharacterManager(&caseFileWriter); + WriteDialogCutsceneManager(&caseFileWriter); + WriteEvidenceManager(&caseFileWriter); + WriteFieldCharacterManager(&caseFileWriter); + WriteFieldCutsceneManager(&caseFileWriter); + WriteFlagManager(&caseFileWriter); + WritePartnerManager(&caseFileWriter); + WriteSpriteManager(&caseFileWriter); + WriteParentLocationListsBySpriteSheetId(&caseFileWriter); + WriteParentLocationListsByVideoId(&caseFileWriter); + + caseFileWriter.EndElement(); + caseArchiveWriter.SaveToFile(caseFileWriter.GetXmlString(), "case.xml"); + caseArchiveWriter.Close(); +} + +void CaseContent::WriteAnimationManager(XmlWriter *pWriter) +{ + pWriter->StartElement("AnimationManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteAudioManager(XmlWriter *pWriter) +{ + pWriter->StartElement("AudioManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteContent(XmlWriter *pWriter) +{ + pWriter->StartElement("Content"); + pWriter->StartElement("LocationByIdHashMap"); + + for (QMap::iterator iter = LocationIdToLocationMap.begin(); iter != LocationIdToLocationMap.end(); iter++) + { + pWriter->StartElement("Entry"); + pWriter->WriteTextElement("Id", iter.key()); + pWriter->EndElement(); + + iter.value()->WriteToCaseXml(pWriter); + } + + pWriter->EndElement(); + pWriter->EndElement(); +} + +void CaseContent::WriteDialogCharacterManager(XmlWriter *pWriter) +{ + pWriter->StartElement("DialogCharacterManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteDialogCutsceneManager(XmlWriter *pWriter) +{ + pWriter->StartElement("DialogCutsceneManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteEvidenceManager(XmlWriter *pWriter) +{ + pWriter->StartElement("EvidenceManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteFieldCharacterManager(XmlWriter *pWriter) +{ + pWriter->StartElement("FieldCharacterManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteFieldCutsceneManager(XmlWriter *pWriter) +{ + pWriter->StartElement("FieldCutsceneManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteFlagManager(XmlWriter *pWriter) +{ + pWriter->StartElement("FlagManager"); + pWriter->EndElement(); +} + +void CaseContent::WritePartnerManager(XmlWriter *pWriter) +{ + pWriter->StartElement("PartnerManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteSpriteManager(XmlWriter *pWriter) +{ + pWriter->StartElement("SpriteManager"); + pWriter->EndElement(); +} + +void CaseContent::WriteParentLocationListsBySpriteSheetId(XmlWriter *pWriter) +{ + pWriter->StartElement("ParentLocationListsBySpriteSheetId"); + pWriter->EndElement(); +} + +void CaseContent::WriteParentLocationListsByVideoId(XmlWriter *pWriter) +{ + pWriter->StartElement("ParentLocationListsByVideoId"); + pWriter->EndElement(); +} + +void CaseContent::AddCharactersFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->DialogCharacterIdToDialogCharacterMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->DialogCharacterIdToDialogCharacterMap.end(); + iter++) + { + Add(iter.key(), new Character(iter.value(), CaseContentLoadingStager::GetCurrent()->FieldCharacterIdToFieldCharacterMap[iter.key()])); + } +} + +void CaseContent::AddLocationsFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->LocationIdToLocationMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->LocationIdToLocationMap.end(); + iter++) + { + Staging::Location *pStagingLocation = iter.value(); + Location *pLocation = new Location(pStagingLocation); + + for (QString cutsceneId : pStagingLocation->CutsceneIdList) + { + pLocation->AddFieldCutscene(new FieldCutscene(CaseContentLoadingStager::GetCurrent()->FieldCutsceneIdToFieldCutsceneMap[cutsceneId])); + } + + Add(iter.key(), pLocation); + } +} + +void CaseContent::AddEncountersFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->EncounterIdToEncounterMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->EncounterIdToEncounterMap.end(); + iter++) + { + Staging::Encounter *pStagingEncounter = iter.value(); + Encounter *pEncounter = new Encounter(pStagingEncounter); + + for (QString conversationId : pStagingEncounter->ConversationIdList) + { + QString realConversationId; + + SplitConversationIdFromCaseFile(conversationId, NULL, &realConversationId); + + pEncounter->AddConversation( + Conversation::CreateFromStaging(CaseContentLoadingStager::GetCurrent()->ConversationIdToConversationMap[conversationId]), + realConversationId); + } + + for (QString interrogationId : pStagingEncounter->InterrogationIdList) + { + QString realInterrogationId; + + SplitConversationIdFromCaseFile(interrogationId, NULL, &realInterrogationId); + + pEncounter->AddInterrogation( + Interrogation::CreateFromStaging(CaseContentLoadingStager::GetCurrent()->InterrogationIdToInterrogationMap[interrogationId]), + realInterrogationId); + } + + for (QString confrontationId : pStagingEncounter->ConfrontationIdList) + { + QString realConfrontationId; + + SplitConversationIdFromCaseFile(confrontationId, NULL, &realConfrontationId); + + pEncounter->AddConfrontation( + Confrontation::CreateFromStaging(CaseContentLoadingStager::GetCurrent()->ConfrontationIdToConfrontationMap[confrontationId]), + realConfrontationId); + } + + if (pStagingEncounter->OneShotConversationId.length() > 0 && pStagingEncounter->pOneShotConversation == NULL) + { + pEncounter->SetOneShotConversation( + Conversation::CreateFromStaging(CaseContentLoadingStager::GetCurrent()->ConversationIdToConversationMap[pStagingEncounter->OneShotConversationId])); + } + + Add(iter.key(), pEncounter); + } +} + +void CaseContent::AddFlagsFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->FlagIdToFlagMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->FlagIdToFlagMap.end(); + iter++) + { + Add(iter.key(), new Flag(iter.value())); + } +} + +void CaseContent::AddEvidencesFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->EvidenceIdToEvidenceMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->EvidenceIdToEvidenceMap.end(); + iter++) + { + Add(iter.key(), new Evidence(iter.value())); + } +} + +void CaseContent::AddBackgroundMusicsFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->BgmIdToBgmFilePathMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->BgmIdToBgmFilePathMap.end(); + iter++) + { + Add(iter.key(), new BackgroundMusic(iter.key(), iter.value())); + } +} + +void CaseContent::AddSoundEffectsFromStager() +{ + for (QMap::iterator iter = CaseContentLoadingStager::GetCurrent()->SfxIdToSfxFilePathMap.begin(); + iter != CaseContentLoadingStager::GetCurrent()->SfxIdToSfxFilePathMap.end(); + iter++) + { + Add(iter.key(), new SoundEffect(iter.key(), iter.value())); + } +} + +QString CaseContent::RelativePathToAbsolutePath(QString relativePath) +{ + return QDir(projectFileDir.absolutePath() + "/" + relativePath).canonicalPath(); +} + +QString CaseContent::AbsolutePathToRelativePath(QString absolutePath) +{ + return projectFileDir.relativeFilePath(absolutePath); +} diff --git a/src/CaseCreator/CaseContent/CaseContent.h b/src/CaseCreator/CaseContent/CaseContent.h new file mode 100644 index 0000000..9493e0a --- /dev/null +++ b/src/CaseCreator/CaseContent/CaseContent.h @@ -0,0 +1,159 @@ +#ifndef CASECONTENT_H +#define CASECONTENT_H + +#include +#include +#include + +#include +#include + +#include "Character.h" +#include "Conversation.h" +#include "Encounter.h" +#include "Location.h" +#include "Flag.h" +#include "Evidence.h" +#include "BackgroundMusic.h" +#include "SoundEffect.h" + +#include "../Utilities/ArchiveReader.h" +#include "../Utilities/Interfaces.h" +#include "XmlReader.h" +#include "XmlWriter.h" + +// To make things easier for us, we'll declare a macro that'll enable us +// to easily do things involving all of the case content types. +// When adding a case content type, make sure that you add an entry here as well; +// the macros using this macro will take care of the rest. +#define CASE_CONTENT_TYPES_OPERATION(OPERATION) \ + OPERATION(Character) \ + OPERATION(Encounter) \ + OPERATION(Location) \ + OPERATION(Flag) \ + OPERATION(Evidence) \ + OPERATION(BackgroundMusic) \ + OPERATION(SoundEffect) + +// We'll also define the names of maps in the CaseContent object +// as IdToMap (e.g., CharacterIdToCharacterMap), for ease of reference. +#define CASE_CONTENT_MAP_NAME(TYPE) TYPE##IdTo##TYPE##Map + +// First, we'll want to declare the maps themselves. +#define DECLARE_CASE_CONTENT_MAP(TYPE) \ + QMap CASE_CONTENT_MAP_NAME(TYPE); + +// Next, we'll want to declare entries in the constructor +// and destructor that will clear the maps and delete their contents. +#define DECLARE_CASE_CONTENT_CONSTRUCTOR_ENTRY(TYPE) \ + CASE_CONTENT_MAP_NAME(TYPE).clear(); + +#define DECLARE_CASE_CONTENT_DESTRUCTOR_ENTRY(TYPE) \ + for (QMap::iterator iter = CASE_CONTENT_MAP_NAME(TYPE).begin(); iter != CASE_CONTENT_MAP_NAME(TYPE).end(); iter++) \ + { \ + delete iter.value(); \ + } \ + \ + CASE_CONTENT_MAP_NAME(TYPE).clear(); + +// We'll also want to declare methods to add types from a case loading stager +// and from a project file, and to save types to a project file. +#define DECLARE_CASE_CONTENT_ADD_FROM_STAGER_PROTOTYPE(TYPE) \ + void Add##TYPE##sFromStager(); + +#define DECLARE_CASE_CONTENT_ADD_FROM_PROJECT_FILE_PROTOTYPE(TYPE) \ + void Add##TYPE##sFromProjectFile(XmlReader *pReader); + +#define DECLARE_CASE_CONTENT_SAVE_TO_PROJECT_FILE_PROTOTYPE(TYPE) \ + void Save##TYPE##sToProjectFile(XmlWriter *pWriter); + +#define DECLARE_CASE_CONTENT_SAVE_TO_CASE_FILE_PROTOTYPE(TYPE) \ + void Save##TYPE##sToCaseFile(XmlWriter *pWriter); + +class CaseContentLoadingStager; + +class CaseContent +{ +public: + static CaseContent * GetInstance() + { + if (pInstance == NULL) + { + pInstance = new CaseContent(); + } + + return pInstance; + } + + static void ReplaceInstance() + { + delete pInstance; + pInstance = new CaseContent(); + } + + static void ReplaceInstanceWithStager(); + static void ReplaceInstanceFromProjectFile(QString filePath); + + void SaveToProjectFile(); + void CompileToCaseFile(QString filePath); + + void WriteAnimationManager(XmlWriter *pWriter); + void WriteAudioManager(XmlWriter *pWriter); + void WriteContent(XmlWriter *pWriter); + void WriteDialogCharacterManager(XmlWriter *pWriter); + void WriteDialogCutsceneManager(XmlWriter *pWriter); + void WriteEvidenceManager(XmlWriter *pWriter); + void WriteFieldCharacterManager(XmlWriter *pWriter); + void WriteFieldCutsceneManager(XmlWriter *pWriter); + void WriteFlagManager(XmlWriter *pWriter); + void WritePartnerManager(XmlWriter *pWriter); + void WriteSpriteManager(XmlWriter *pWriter); + void WriteParentLocationListsBySpriteSheetId(XmlWriter *pWriter); + void WriteParentLocationListsByVideoId(XmlWriter *pWriter); + + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_ADD_FROM_STAGER_PROTOTYPE) + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_ADD_FROM_PROJECT_FILE_PROTOTYPE) + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_SAVE_TO_PROJECT_FILE_PROTOTYPE) + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_SAVE_TO_CASE_FILE_PROTOTYPE) + + // NOTE: Passing in the pointer from which to retrieve or to add things is super ugly, + // but I can't think of a better way of doing it - there's no way to make a templated + // virtual function in an interface, so this is the best I could come up with. + // + // If anyone wants to have a go at making this less hacky, be my guest. + // + template + void Add(QString id, T *pObject, void *pParentObject = NULL); + + template + T * GetById(QString id, void *pParentObject = NULL); + + template + QStringList GetIds(void *pParentObject = NULL); + + template + QStringList GetDisplayNames(void *pParentObject = NULL); + + QString RelativePathToAbsolutePath(QString relativePath); + QString AbsolutePathToRelativePath(QString absolutePath); + +private: + static CaseContent *pInstance; + + CaseContent() + { + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_CONSTRUCTOR_ENTRY) + } + + ~CaseContent() + { + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_DESTRUCTOR_ENTRY) + } + + CASE_CONTENT_TYPES_OPERATION(DECLARE_CASE_CONTENT_MAP) + + QDir projectFileDir; + QString projectFilePath; +}; + +#endif // CASECONTENT_H diff --git a/src/CaseCreator/CaseContent/Character.cpp b/src/CaseCreator/CaseContent/Character.cpp new file mode 100644 index 0000000..1819612 --- /dev/null +++ b/src/CaseCreator/CaseContent/Character.cpp @@ -0,0 +1,1303 @@ +#include "Character.h" + +#include "CaseContent.h" +#include "XmlReader.h" +#include "XmlWriter.h" +#include "MLIException.h" + +#include "Condition.h" + +#include "Staging/CaseContentLoadingStager.h" +#include "Staging/DialogCharacter.Staging.h" +#include "Staging/Enums.Staging.h" + +#include "CaseCreator/UIComponents/LocationTab/FieldCharacterManipulator.h" +#include "CaseCreator/UIComponents/CharacterTab/CharacterDialogSpriteManipulator.h" + +Character::Character() +{ + pHitBox = NULL; + + for (int i = 0; i < Staging::FieldCharacterDirection_Count; i++) + { + standingAnimations[i] = new Animation(); + walkingAnimations[i] = new Animation(); + runningAnimations[i] = new Animation(); + } + + standingSprite.SetId("Standing"); + walkingSprite.SetId("Walking"); + runningSprite.SetId("Running"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + standingSprite.SetAnimationForDirection(static_cast(i), &standingAnimations[i]); + walkingSprite.SetAnimationForDirection(static_cast(i), &walkingAnimations[i]); + runningSprite.SetAnimationForDirection(static_cast(i), &runningAnimations[i]); + } +} + +Character::Character(QString id, QString name, QString defaultEmotionId) + : Character() +{ + this->id = id; + this->name = name; + this->defaultEmotionId = defaultEmotionId; + + this->AddDialogEmotion(defaultEmotionId); +} + +Character::Character(Staging::DialogCharacter *pStagingDialogCharacter, Staging::FieldCharacter *pStagingFieldCharacter) + : Character() +{ + id = pStagingDialogCharacter->Id; + + if (id != pStagingFieldCharacter->Id) + { + throw new MLIException("Incompatible dialog and field characters used together."); + } + + name = pStagingFieldCharacter->Name; + defaultEmotionId = pStagingDialogCharacter->DefaultEmotionId; + + for (QMap::iterator iter = pStagingDialogCharacter->CharacterEmotionBaseSpriteIds.begin(); + iter != pStagingDialogCharacter->CharacterEmotionBaseSpriteIds.end(); + iter++) + { + QString emotionId = iter.key(); + DialogEmotion *pEmotion = GetDialogEmotion(emotionId); + + pEmotion->SetBaseFilePath(CaseContentLoadingStager::GetCurrent()->GetSpriteFilePathFromId(iter.value())); + + if (pStagingDialogCharacter->CharacterEmotionEyeSpriteIds.contains(emotionId)) + { + for (QString eyeSpriteId : pStagingDialogCharacter->CharacterEmotionEyeSpriteIds[emotionId]) + { + pEmotion->AddEyeFrame(CaseContentLoadingStager::GetCurrent()->GetSpriteFilePathFromId(eyeSpriteId)); + } + } + + if (pStagingDialogCharacter->CharacterEmotionMouthSpriteIds.contains(emotionId)) + { + for (QString mouthSpriteId : pStagingDialogCharacter->CharacterEmotionMouthSpriteIds[emotionId]) + { + pEmotion->AddMouthFrame(CaseContentLoadingStager::GetCurrent()->GetSpriteFilePathFromId(mouthSpriteId)); + } + } + } + + for (int i = 0; i < Staging::FieldCharacterDirection_Count; i++) + { + if (pStagingFieldCharacter->CharacterStandingAnimationIds[i].length() > 0) + { + standingAnimations[i] = new Animation(CaseContentLoadingStager::GetCurrent()->AnimationIdToAnimationMap[pStagingFieldCharacter->CharacterStandingAnimationIds[i]]); + } + else + { + standingAnimations[i] = NULL; + } + + if (pStagingFieldCharacter->CharacterWalkingAnimationIds[i].length() > 0) + { + walkingAnimations[i] = new Animation(CaseContentLoadingStager::GetCurrent()->AnimationIdToAnimationMap[pStagingFieldCharacter->CharacterWalkingAnimationIds[i]]); + } + else + { + walkingAnimations[i] = NULL; + } + + if (pStagingFieldCharacter->CharacterRunningAnimationIds[i].length() > 0) + { + runningAnimations[i] = new Animation(CaseContentLoadingStager::GetCurrent()->AnimationIdToAnimationMap[pStagingFieldCharacter->CharacterRunningAnimationIds[i]]); + } + else + { + runningAnimations[i] = NULL; + } + } + + pHitBox = new HitBox(pStagingFieldCharacter->pHitBox); + anchorPosition = pStagingFieldCharacter->AnchorPosition; + clickRect = pStagingFieldCharacter->ClickRect; +} + +Character::~Character() +{ + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + if (standingAnimations[i] != NULL) + { + delete standingAnimations[i]; + standingAnimations[i] = NULL; + } + + if (walkingAnimations[i] != NULL) + { + delete walkingAnimations[i]; + walkingAnimations[i] = NULL; + } + + if (runningAnimations[i] != NULL) + { + delete runningAnimations[i]; + runningAnimations[i] = NULL; + } + } + + delete pHitBox; + pHitBox = NULL; + + for (QString emotionId : emotionByIdMap.keys()) + { + delete emotionByIdMap[emotionId]; + } + + emotionByIdMap.clear(); +} + +void Character::SaveElementsToXml(XmlWriter *pWriter) +{ + XmlStorableObject::SaveElementsToXml(pWriter); + + pWriter->StartElement("FieldAnimations"); + + pWriter->StartElement("StandingAnimations"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + if (standingAnimations[i] != NULL) + { + pWriter->StartElement("StandingAnimation"); + + pWriter->WriteTextElement("Direction", FieldCharacterDirectionToString((FieldCharacterDirection)i)); + pWriter->StartElement("Animation"); + standingAnimations[i]->SaveToXml(pWriter); + pWriter->EndElement(); + + pWriter->EndElement(); + } + } + + pWriter->EndElement(); + + pWriter->StartElement("WalkingAnimations"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + if (walkingAnimations[i] != NULL) + { + pWriter->StartElement("WalkingAnimation"); + + pWriter->WriteTextElement("Direction", FieldCharacterDirectionToString((FieldCharacterDirection)i)); + pWriter->StartElement("Animation"); + walkingAnimations[i]->SaveToXml(pWriter); + pWriter->EndElement(); + + pWriter->EndElement(); + } + } + + pWriter->EndElement(); + + pWriter->StartElement("RunningAnimations"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + if (runningAnimations[i] != NULL) + { + pWriter->StartElement("RunningAnimation"); + + pWriter->WriteTextElement("Direction", FieldCharacterDirectionToString((FieldCharacterDirection)i)); + pWriter->StartElement("Animation"); + runningAnimations[i]->SaveToXml(pWriter); + pWriter->EndElement(); + + pWriter->EndElement(); + } + } + + pWriter->EndElement(); + + pWriter->EndElement(); +} + +void Character::LoadElementsFromXml(XmlReader *pReader) +{ + XmlStorableObject::LoadElementsFromXml(pReader); + + emotionIdList.clear(); + + for (QString emotionId : emotionByIdMap.keys()) + { + emotionIdList.push_back(emotionId); + } + + pReader->StartElement("FieldAnimations"); + + pReader->StartElement("StandingAnimations"); + pReader->StartList("StandingAnimation"); + + while (pReader->MoveToNextListItem()) + { + FieldCharacterDirection direction = StringToFieldCharacterDirection(pReader->ReadTextElement("Direction")); + + pReader->StartElement("Animation"); + standingAnimations[direction] = new Animation(pReader); + pReader->EndElement(); + } + + pReader->EndElement(); + + pReader->StartElement("WalkingAnimations"); + pReader->StartList("WalkingAnimation"); + + while (pReader->MoveToNextListItem()) + { + FieldCharacterDirection direction = StringToFieldCharacterDirection(pReader->ReadTextElement("Direction")); + + pReader->StartElement("Animation"); + walkingAnimations[direction] = new Animation(pReader); + pReader->EndElement(); + } + + pReader->EndElement(); + + pReader->StartElement("RunningAnimations"); + pReader->StartList("RunningAnimation"); + + while (pReader->MoveToNextListItem()) + { + FieldCharacterDirection direction = StringToFieldCharacterDirection(pReader->ReadTextElement("Direction")); + + pReader->StartElement("Animation"); + runningAnimations[direction] = new Animation(pReader); + pReader->EndElement(); + } + + pReader->EndElement(); + + pReader->EndElement(); +} + +QString Character::DialogEmotion::GetBaseFilePath() +{ + return baseFilePath; +} + +QString Character::DialogEmotion::GetEyeFilePath(int eyeIndex) +{ + return + this->eyeFilePathList.count() > eyeIndex ? + this->eyeFilePathList[eyeIndex] : + ""; +} + +QStringList Character::DialogEmotion::GetEyeFilePaths() +{ + return eyeFilePathList; +} + +QString Character::DialogEmotion::GetMouthFilePath(int mouthIndex) +{ + return + this->mouthFilePathList.count() > mouthIndex ? + this->mouthFilePathList[mouthIndex] : + ""; +} + +QString Character::DialogEmotion::GetRandomMouthFilePath() +{ + if (this->mouthFilePathList.count() == 0) + { + return ""; + } + + int newMouthIndex = this->lastMouthIndex; + + while (this->lastMouthIndex == newMouthIndex) + { + newMouthIndex = qrand() % this->mouthFilePathList.count(); + } + + this->lastMouthIndex = newMouthIndex; + return this->mouthFilePathList[this->lastMouthIndex]; +} + +QStringList Character::DialogEmotion::GetMouthFilePaths() +{ + return mouthFilePathList; +} + +int Character::DialogEmotion::GetEyeDuration(int eyeIndex) +{ + return eyeIndex > 0 ? 75 : -1; +} + +int Character::DialogEmotion::GetEyeFrameCount() +{ + return this->eyeFilePathList.count(); +} + +int Character::DialogEmotion::GetMouthFrameCount() +{ + return this->mouthFilePathList.count(); +} + +void Character::DialogEmotion::SetBaseFilePath(QString filePath) +{ + this->baseFilePath = filePath; +} + +void Character::DialogEmotion::AddEyeFrame(QString filePath) +{ + this->eyeFilePathList.push_back(filePath); +} + +void Character::DialogEmotion::SetEyeFrame(int index, QString filePath) +{ + this->eyeFilePathList[index] = filePath; +} + +void Character::DialogEmotion::SetEyeFrames(QStringList filePaths) +{ + this->eyeFilePathList = filePaths; +} + +void Character::DialogEmotion::InsertEyeFrame(int index, QString filePath) +{ + this->eyeFilePathList.insert(index, filePath); +} + +void Character::DialogEmotion::RemoveEyeFrame(int index) +{ + this->eyeFilePathList.removeAt(index); +} + +void Character::DialogEmotion::AddMouthFrame(QString filePath) +{ + this->mouthFilePathList.push_back(filePath); +} + +void Character::DialogEmotion::SetMouthFrame(int index, QString filePath) +{ + this->mouthFilePathList[index] = filePath; +} + +void Character::DialogEmotion::SetMouthFrames(QStringList filePaths) +{ + this->mouthFilePathList = filePaths; +} + +void Character::DialogEmotion::InsertMouthFrame(int index, QString filePath) +{ + this->mouthFilePathList.insert(index, filePath); +} + +void Character::DialogEmotion::RemoveMouthFrame(int index) +{ + this->mouthFilePathList.removeAt(index); +} + +Character::DialogEmotion::DrawingView::DrawingView(Character::DialogEmotion *pEmotion, IDrawable *pParent) + : IDrawable(pParent) + , IDrawingView(pEmotion, pParent) +{ + pBodyBasePixmapItem = new QGraphicsPixmapItem(); + pEyeDrawable = new EyeDrawable(pEmotion, this); + pMouthDrawable = new MouthDrawable(pEmotion, this); +} + +Character::DialogEmotion::DrawingView::~DrawingView() +{ + delete pEyeDrawable; + pEyeDrawable = NULL; + + delete pMouthDrawable; + pMouthDrawable = NULL; +} + +void Character::DialogEmotion::DrawingView::DrawCore(QGraphicsScene *pScene, QList &addedItems) +{ + if (pObject->GetBaseFilePath().length() > 0) + { + pBodyBasePixmapItem->setPixmap(QPixmap(pObject->GetBaseFilePath())); + + pScene->addItem(pBodyBasePixmapItem); + addedItems.append(pBodyBasePixmapItem); + } +} + +void Character::DialogEmotion::DrawingView::DrawChildren(QGraphicsScene *pScene) +{ + pEyeDrawable->Draw(pScene); + pMouthDrawable->Draw(pScene); +} + +void Character::DialogEmotion::DrawingView::UpdateChildren() +{ + pEyeDrawable->Update(); + pMouthDrawable->Update(); +} + +void Character::DialogEmotion::DrawingView::ResetChildren() +{ + pEyeDrawable->Reset(); + pMouthDrawable->Reset(); +} + +void Character::DialogEmotion::DrawingView::SetIsSpeaking(bool isSpeaking) +{ + pMouthDrawable->SetIsSpeaking(isSpeaking); +} + +Character::DialogEmotion::DrawingView::EyeDrawable::EyeDrawable(Character::DialogEmotion *pEmotion, IDrawable *pParent) + : IDrawable(pParent) +{ + this->pEmotion = pEmotion; + currentEyeIndex = 0; +} + +void Character::DialogEmotion::DrawingView::EyeDrawable::DrawCore(QGraphicsScene *pScene, QList &addedItems) +{ + for (const QString &eyeFilePath : pEmotion->GetEyeFilePaths()) + { + eyePixmapItems.append(new QGraphicsPixmapItem(QPixmap(eyeFilePath))); + } + + for (QGraphicsPixmapItem *pEyePixmapItem : eyePixmapItems) + { + pScene->addItem(pEyePixmapItem); + addedItems.append(pEyePixmapItem); + } + + currentEyeIndex = 0; + + if (eyePixmapItems.size() > 0) + { + UpdateTimeout(); + } +} + +void Character::DialogEmotion::DrawingView::EyeDrawable::UpdateCore() +{ + for (int i = 0; i < eyePixmapItems.count(); i++) + { + if (i != currentEyeIndex) + { + eyePixmapItems[i]->setOpacity(0.0); + } + } +} + +void Character::DialogEmotion::DrawingView::EyeDrawable::UpdateOnTimeout() +{ + currentEyeIndex = (currentEyeIndex + 1) % pEmotion->GetEyeFrameCount(); + UpdateTimeout(); +} + +void Character::DialogEmotion::DrawingView::EyeDrawable::UpdateTimeout() +{ + msTimeoutDuration = pEmotion->GetEyeDuration(currentEyeIndex); + + // A value of -1 indicates that it should be randomized. + if (msTimeoutDuration < 0) + { + msTimeoutDuration = (int)(qrand() % 2001) + 2000; + } +} + +Character::DialogEmotion::DrawingView::MouthDrawable::MouthDrawable(Character::DialogEmotion *pEmotion, IDrawable *pParent) + : IDrawable(pParent) +{ + this->pEmotion = pEmotion; + currentMouthIndex = -1; + isSpeaking = false; +} + +void Character::DialogEmotion::DrawingView::MouthDrawable::DrawCore(QGraphicsScene *pScene, QList &addedItems) +{ + for (const QString &mouthFilePath : pEmotion->GetMouthFilePaths()) + { + mouthPixmapItems.append(new QGraphicsPixmapItem(QPixmap(mouthFilePath))); + } + + for (QGraphicsPixmapItem *pMouthPixmapItem : mouthPixmapItems) + { + pScene->addItem(pMouthPixmapItem); + addedItems.append(pMouthPixmapItem); + } + + currentMouthIndex = -1; + + if (mouthPixmapItems.size() > 0) + { + UpdateOnTimeout(); + } +} + +void Character::DialogEmotion::DrawingView::MouthDrawable::UpdateCore() +{ + for (int i = 0; i < mouthPixmapItems.count(); i++) + { + if (i != currentMouthIndex) + { + mouthPixmapItems[i]->setOpacity(0.0); + } + } +} + +void Character::DialogEmotion::DrawingView::MouthDrawable::UpdateOnTimeout() +{ + if (isSpeaking) + { + int newMouthIndex = currentMouthIndex; + + while (currentMouthIndex == newMouthIndex) + { + newMouthIndex = qrand() % pEmotion->GetMouthFrameCount(); + } + + currentMouthIndex = newMouthIndex; + msTimeoutDuration = 100; + } + else + { + currentMouthIndex = 0; + } +} + +void Character::DialogEmotion::DrawingView::MouthDrawable::SetIsSpeaking(bool isSpeaking) +{ + if (this->isSpeaking != isSpeaking) + { + this->isSpeaking = isSpeaking; + + if (mouthPixmapItems.size() > 0) + { + if (this->isSpeaking) + { + msTimeoutDuration = 100; + } + else + { + msTimeoutDuration = 0; + } + + UpdateOnTimeout(); + Update(); + } + } +} + +void Character::AddDialogEmotion(QString emotionId) +{ + // If we don't already have a dialog emotion for this ID, + // we'll create one and add it to the map. + if (!this->emotionIdList.contains(emotionId)) + { + this->emotionIdList.push_back(emotionId); + } + else + { + throw new MLIException("The emotion ID list and the emotion map by ID should always be in sync."); + } + + Character::DialogEmotion *pDialogEmotion = new Character::DialogEmotion(emotionId); + this->emotionByIdMap.insert(emotionId, pDialogEmotion); +} + +Character::DialogEmotion * Character::GetDialogEmotion(QString emotionId) +{ + if (this->emotionByIdMap.contains(emotionId)) + { + return this->emotionByIdMap[emotionId]; + } + else + { + AddDialogEmotion(emotionId); + return this->emotionByIdMap[emotionId]; + } +} + +void Character::SetDialogEmotion(QString emotionId, DialogEmotion *pDialogEmotion) +{ + // If we don't already have a dialog emotion for this ID, + // we'll create one and add it to the map. + if (!this->emotionIdList.contains(emotionId)) + { + this->emotionIdList.push_back(emotionId); + } + + pDialogEmotion->SetId(emotionId); + this->emotionByIdMap[emotionId] = pDialogEmotion; +} + +Animation * Character::GetStandingAnimationForDirection(FieldCharacterDirection direction) +{ + return this->standingAnimations[direction]; +} + +Animation * Character::GetWalkingAnimationForDirection(FieldCharacterDirection direction) +{ + return this->walkingAnimations[direction]; +} + +Animation * Character::GetRunningAnimationForDirection(FieldCharacterDirection direction) +{ + return this->runningAnimations[direction]; +} + +Character::FieldInstance::FieldInstance(Staging::FieldCharacter *pStagingFieldCharacter) +{ + pCharacter = CaseContent::GetInstance()->GetById(pStagingFieldCharacter->Id); + + position = pStagingFieldCharacter->Position; + direction = (CharacterDirection)pStagingFieldCharacter->Direction; + spriteDirection = (FieldCharacterDirection)pStagingFieldCharacter->SpriteDirection; + clickEncounterId = pStagingFieldCharacter->ClickEncounterId; + clickCutsceneId = pStagingFieldCharacter->ClickCutsceneId; + interactionLocation = pStagingFieldCharacter->InteractionLocation; + interactFromAnywhere = pStagingFieldCharacter->InteractFromAnywhere; + pCondition = new Condition(pStagingFieldCharacter->pCondition); +} + +Character::FieldInstance::~FieldInstance() +{ + delete pCondition; + pCondition = NULL; +} + +void Character::FieldInstance::WriteToCaseXml(XmlWriter *pWriter) +{ + pWriter->StartElement("FieldCharacter"); + pWriter->WriteTextElement("Id", pCharacter->id); + pWriter->WriteTextElement("Name", pCharacter->name); + + pWriter->StartElement("CharacterStandingAnimationIds"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + FieldCharacterDirection direction = static_cast(i); + + if (pCharacter->standingAnimations[direction] != NULL) + { + QString directionString = FieldCharacterDirectionToString(direction); + + pWriter->StartElement("Entry"); + pWriter->WriteTextElement("Direction", directionString); + pWriter->WriteTextElement("Id", QString(pCharacter->id) + QString("StandingAnimation") + directionString); + pWriter->EndElement(); + } + } + + pWriter->EndElement(); + + pWriter->StartElement("CharacterWalkingAnimationIds"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + FieldCharacterDirection direction = static_cast(i); + + if (pCharacter->walkingAnimations[direction] != NULL) + { + QString directionString = FieldCharacterDirectionToString(direction); + + pWriter->StartElement("Entry"); + pWriter->WriteTextElement("Direction", directionString); + pWriter->WriteTextElement("Id", QString(pCharacter->id) + QString("WalkingAnimation") + directionString); + pWriter->EndElement(); + } + } + + pWriter->EndElement(); + + pWriter->StartElement("CharacterRunningAnimationIds"); + + for (int i = 0; i < FieldCharacterDirectionCount; i++) + { + FieldCharacterDirection direction = static_cast(i); + + if (pCharacter->walkingAnimations[direction] != NULL) + { + QString directionString = FieldCharacterDirectionToString(direction); + + pWriter->StartElement("Entry"); + pWriter->WriteTextElement("Direction", directionString); + pWriter->WriteTextElement("Id", QString(pCharacter->id) + QString("RunningAnimation") + directionString); + pWriter->EndElement(); + } + } + + pWriter->EndElement(); + + pWriter->StartElement("Position"); + position.SaveToXml(pWriter); + pWriter->EndElement(); + + pWriter->StartElement("HitBox"); + // Placeholder + pWriter->EndElement(); + + pWriter->WriteTextElement("Direction", CharacterDirectionToString(direction)); + pWriter->WriteTextElement("SpriteDirection", FieldCharacterDirectionToString(spriteDirection)); + + pWriter->StartElement("ClickRect"); + //clickRect.SaveToXml(pWriter); // Placeholder + pWriter->EndElement(); + + if (clickCutsceneId.length() > 0) + { + pWriter->WriteTextElement("ClickCutsceneId", clickCutsceneId); + } + + if (clickEncounterId.length() > 0) + { + pWriter->WriteTextElement("ClickEncounterId", clickEncounterId); + } + + pWriter->WriteBooleanElement("InteractFromAnywhere", interactFromAnywhere); + + pWriter->EndElement(); +} + +QString Character::FieldInstance::GetId() +{ + return pCharacter->GetId(); +} + +QString Character::FieldInstance::GetName() +{ + return pCharacter->GetName(); +} + +void Character::FieldInstance::SetName(QString name) +{ + pCharacter->SetName(name); + emit PropertyChanged("Name"); +} + +QString Character::FieldInstance::GetDisplayName() +{ + return pCharacter->GetDisplayName(); +} + +void Character::FieldInstance::SetPosition(Vector2 position) +{ + this->position = position; + emit PropertyChanged("Position"); +} + +Vector2 Character::FieldInstance::GetMidpoint() const +{ + return GetMidpointForPosition(position); +} + +Vector2 Character::FieldInstance::GetMidpointForPosition(const Vector2 &position) const +{ + return + Vector2( + position.GetX() + pCharacter->GetClickRect().GetX() + pCharacter->GetClickRect().GetWidth() / 2, + position.GetY() + pCharacter->GetClickRect().GetY() + pCharacter->GetClickRect().GetHeight() / 2); // TODO: Subtract the "extra height" once we have it. +} + +void Character::FieldInstance::SaveElementsToXml(XmlWriter *pWriter) +{ + pWriter->WriteTextElement("CharacterId", pCharacter->GetId()); + XmlStorableObject::SaveElementsToXml(pWriter); +} + +void Character::FieldInstance::LoadElementsFromXml(XmlReader *pReader) +{ + pCharacter = CaseContent::GetInstance()->GetById(pReader->ReadTextElement("CharacterId")); + XmlStorableObject::LoadElementsFromXml(pReader); +} + +ObjectManipulatorSlots *Character::FieldInstance::GetManipulationWidget() +{ + FieldCharacterManipulator *pManipulator = FieldCharacterManipulator::Create(); + pManipulator->Init(this); + return pManipulator; +} + +Character::FieldInstance::ManipulationView::ManipulationView(Character::FieldInstance *pCharacterFieldInstance, IDrawable *pParent, UndoCommandSink *pUndoCommandSink) + : IDrawable(pParent) + , IManipulationView(pCharacterFieldInstance, pParent, pUndoCommandSink) +{ + pSpriteAnimationDrawingViews[FieldCharacterDirectionUp] = pObject->pCharacter->GetStandingAnimationForDirection(FieldCharacterDirectionUp)->GetDrawingView(this); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp] = pObject->pCharacter->GetStandingAnimationForDirection(FieldCharacterDirectionDiagonalUp)->GetDrawingView(this); + pSpriteAnimationDrawingViews[FieldCharacterDirectionSide] = pObject->pCharacter->GetStandingAnimationForDirection(FieldCharacterDirectionSide)->GetDrawingView(this); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown] = pObject->pCharacter->GetStandingAnimationForDirection(FieldCharacterDirectionDiagonalDown)->GetDrawingView(this); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDown] = pObject->pCharacter->GetStandingAnimationForDirection(FieldCharacterDirectionDown)->GetDrawingView(this); + + positionIsOverridden = false; + directionIsOverridden = false; + spriteDirectionIsOverridden = false; + + pManipulationViewBox = new ManipulationViewBox(this); + UpdateAnimationDrawingView(); +} + +Character::FieldInstance::ManipulationView::~ManipulationView() +{ + delete pSpriteAnimationDrawingViews[FieldCharacterDirectionUp]; + delete pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp]; + delete pSpriteAnimationDrawingViews[FieldCharacterDirectionSide]; + delete pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown]; + delete pSpriteAnimationDrawingViews[FieldCharacterDirectionDown]; + pSpriteAnimationDrawingViews[FieldCharacterDirectionUp] = NULL; + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp] = NULL; + pSpriteAnimationDrawingViews[FieldCharacterDirectionSide] = NULL; + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown] = NULL; + pSpriteAnimationDrawingViews[FieldCharacterDirectionDown] = NULL; + + delete pManipulationViewBox; + pManipulationViewBox = NULL; +} + +void Character::FieldInstance::ManipulationView::DrawCore(QGraphicsScene */*pScene*/, QList &/*addedItems*/) +{ +} + +void Character::FieldInstance::ManipulationView::DrawChildren(QGraphicsScene *pScene) +{ + pSpriteAnimationDrawingViews[FieldCharacterDirectionUp]->Draw(pScene); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp]->Draw(pScene); + pSpriteAnimationDrawingViews[FieldCharacterDirectionSide]->Draw(pScene); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown]->Draw(pScene); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDown]->Draw(pScene); + + pManipulationViewBox->SetBoxRect(GetBoundingRect()); + pManipulationViewBox->Draw(pScene); +} + +void Character::FieldInstance::ManipulationView::UpdateChildren() +{ + pSpriteAnimationDrawingViews[FieldCharacterDirectionUp]->Update(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp]->Update(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionSide]->Update(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown]->Update(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDown]->Update(); + + pManipulationViewBox->Update(); +} + +void Character::FieldInstance::ManipulationView::ResetChildren() +{ + pSpriteAnimationDrawingViews[FieldCharacterDirectionUp]->Reset(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp]->Reset(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionSide]->Reset(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown]->Reset(); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDown]->Reset(); + + pManipulationViewBox->Reset(); +} + +RectangleWH Character::FieldInstance::ManipulationView::GetBoundingRect() +{ + RectangleWH clickRect = pObject->pCharacter->GetClickRect(); + + if (clickRect.GetWidth() > 0 && clickRect.GetHeight() > 0) + { + return clickRect; + } + else + { + return IDrawable::GetBoundingRect(); + } +} + +qreal Character::FieldInstance::ManipulationView::GetZOrder() +{ + // Multiply by 100 in order to allow intraelement granularity - + // e.g., enabling ForegroundElementAnimations to always be drawn on top of + // their ForegroundElements without causing them to also be drawn on top of + // other ForegroundElements. + return 100 * (pObject->position.GetY() + pObject->pCharacter->GetAnchorPosition()); +} + +void Character::FieldInstance::ManipulationView::SetShouldDimIfDisabled(bool shouldDimIfDisabled) +{ + this->shouldDimIfDisabled = shouldDimIfDisabled; + Update(); +} + +void Character::FieldInstance::ManipulationView::SetPositionOverride(Vector2 position) +{ + if (positionOverride != position) + { + positionIsOverridden = true; + positionOverride = position; + + Update(); + + emit PositionOverrideChanged(positionOverride); + } +} + +void Character::FieldInstance::ManipulationView::ClearPositionOverride() +{ + positionIsOverridden = false; + + Update(); +} + +void Character::FieldInstance::ManipulationView::SetDirectionOverride(CharacterDirection direction) +{ + directionIsOverridden = true; + directionOverride = direction; + + Update(); +} + +void Character::FieldInstance::ManipulationView::ClearDirectionOverride() +{ + directionIsOverridden = false; + + Update(); +} + +void Character::FieldInstance::ManipulationView::SetSpriteDirectionOverride(FieldCharacterDirection direction) +{ + spriteDirectionIsOverridden = true; + spriteDirectionOverride = direction; + + UpdateAnimationDrawingView(); + Redraw(); +} + +void Character::FieldInstance::ManipulationView::ClearSpriteDirectionOverride() +{ + spriteDirectionIsOverridden = false; + + UpdateAnimationDrawingView(); + Redraw(); +} + +void Character::FieldInstance::ManipulationView::OnGotFocus() +{ + SetIsSelected(true); +} + +void Character::FieldInstance::ManipulationView::OnLostFocus() +{ + SetIsSelected(false); +} + +void Character::FieldInstance::ManipulationView::OnMouseEnter() +{ + SetIsMouseOver(true); +} + +void Character::FieldInstance::ManipulationView::OnMouseLeave() +{ + SetIsMouseOver(false); +} + +void Character::FieldInstance::ManipulationView::OnMousePress() +{ + if (positionIsOverridden) + { + positionBeforeDrag = positionOverride; + } + else + { + positionBeforeDrag = this->pObject->position; + } +} + +void Character::FieldInstance::ManipulationView::OnMouseDrag(Vector2 delta) +{ + SetIsBeingDragged(true); + + if (positionIsOverridden) + { + SetPositionOverride(positionOverride + delta); + } + else + { + SetPosition(this->pObject->position + delta); + } +} + +void Character::FieldInstance::ManipulationView::OnMouseRelease() +{ + SetIsBeingDragged(false); + + if (positionIsOverridden && positionOverride != positionBeforeDrag) + { + AddUndoCommand(new MoveUndoCommand(this, positionBeforeDrag, positionOverride, true /* isPositionOverride */)); + } + else if (!positionIsOverridden && this->pObject->position != positionBeforeDrag) + { + AddUndoCommand(new MoveUndoCommand(this, positionBeforeDrag, this->pObject->position)); + } +} + +void Character::FieldInstance::ManipulationView::SetPosition(Vector2 position) +{ + this->pObject->SetPosition(position); + Update(); +} + +void Character::FieldInstance::ManipulationView::SetIsMouseOver(bool isMouseOver) +{ + pManipulationViewBox->SetIsMouseOver(isMouseOver); +} + +void Character::FieldInstance::ManipulationView::SetIsSelected(bool isSelected) +{ + pManipulationViewBox->SetIsSelected(isSelected); +} + +void Character::FieldInstance::ManipulationView::SetIsBeingDragged(bool isBeingDragged) +{ + pManipulationViewBox->SetIsBeingDragged(isBeingDragged); +} + +void Character::FieldInstance::ManipulationView::UpdateAnimationDrawingView() +{ + FieldCharacterDirection spriteDirection = spriteDirectionIsOverridden ? spriteDirectionOverride : pObject->GetSpriteDirection(); + + pSpriteAnimationDrawingViews[FieldCharacterDirectionUp]->SetOpacity(spriteDirection == FieldCharacterDirectionUp ? 1.0 : 0.0); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalUp]->SetOpacity(spriteDirection == FieldCharacterDirectionDiagonalUp ? 1.0 : 0.0); + pSpriteAnimationDrawingViews[FieldCharacterDirectionSide]->SetOpacity(spriteDirection == FieldCharacterDirectionSide ? 1.0 : 0.0); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDiagonalDown]->SetOpacity(spriteDirection == FieldCharacterDirectionDiagonalDown ? 1.0 : 0.0); + pSpriteAnimationDrawingViews[FieldCharacterDirectionDown]->SetOpacity(spriteDirection == FieldCharacterDirectionDown ? 1.0 : 0.0); + + Update(); +} + +GET_MANIPULATABLE_OBJECT_FROM_DEFINITION(Character::FieldInstance) + +Character::DialogDrawingView::DialogDrawingView(Character *pCharacter, IDrawable *pParent) + : IDrawable(pParent) + , IDrawingView(pCharacter, pParent) +{ + position = Vector2(0, 0); + isFlipped = false; + + pEmotionDrawingView = NULL; +} + +Character::DialogDrawingView::~DialogDrawingView() +{ + delete pEmotionDrawingView; + pEmotionDrawingView = NULL; +} + +void Character::DialogDrawingView::DrawChildren(QGraphicsScene *pScene) +{ + pEmotionDrawingView->Draw(pScene); +} + +void Character::DialogDrawingView::UpdateChildren() +{ + pEmotionDrawingView->Update(); +} + +void Character::DialogDrawingView::ResetChildren() +{ + pEmotionDrawingView->Reset(); +} + +void Character::DialogDrawingView::SetEmotion(QString emotionId) +{ + if (currentEmotionId == emotionId) + { + return; + } + + if (pEmotionDrawingView != NULL) + { + pEmotionDrawingView->Reset(); + + delete pEmotionDrawingView; + pEmotionDrawingView = NULL; + } + + Character::DialogEmotion *pEmotion = pObject->GetDialogEmotion(emotionId); + + if (pEmotion != NULL) + { + pEmotionDrawingView = pEmotion->GetDrawingView(this); + currentEmotionId = emotionId; + } + + Redraw(); +} + +void Character::DialogDrawingView::SetIsSpeaking(bool isSpeaking) +{ + pEmotionDrawingView->SetIsSpeaking(isSpeaking); +} + +CharacterSelector::CharacterSelector(bool allowNoCharacter, QWidget *parent) + : QComboBox(parent) +{ + characterIds = CaseContent::GetInstance()->GetIds(); + this->allowNoCharacter = allowNoCharacter; + + if (allowNoCharacter) + { + addItem("(no character)"); + } + + for (QString characterId : characterIds) + { + QString displayName = CaseContent::GetInstance()->GetById(characterId)->GetDisplayName(); + characterDisplayNames.append(displayName); + addItem(displayName); + } + + QObject::connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(CurrentIndexChanged(int))); +} + +QString CharacterSelector::GetSelectedCharacterId() +{ + if (currentIndex() == 0 && allowNoCharacter) + { + return ""; + } + else + { + return characterIds[currentIndex() - (allowNoCharacter ? 1 : 0)]; + } +} + +void CharacterSelector::SetToCharacterById(const QString &id) +{ + int indexOfCharacter = characterIds.indexOf(QRegExp(id, Qt::CaseInsensitive)); + + if (indexOfCharacter >= 0) + { + setCurrentIndex(indexOfCharacter + (allowNoCharacter ? 1 : 0)); + } + else if (allowNoCharacter) + { + setCurrentIndex(0); + } +} + +void CharacterSelector::CurrentIndexChanged(int /*currentIndex*/) +{ + emit CharacterSelected(GetSelectedCharacterId()); +} + +template <> +void CaseContent::Add(QString id, Character::DialogEmotion *pDialogEmotion, void *pParentObject) +{ + Character *pCharacter = reinterpret_cast(pParentObject); + + if (pCharacter != NULL) + { + pCharacter->SetDialogEmotion(id, pDialogEmotion); + } +} + +template <> +Character::DialogEmotion * CaseContent::GetById(QString id, void *pParentObject) +{ + Character *pCharacter = reinterpret_cast(pParentObject); + + if (pCharacter != NULL) + { + return pCharacter->GetDialogEmotion(id); + } + else + { + return NULL; + } +} + +template <> +QStringList CaseContent::GetIds(void *pParentObject) +{ + Character *pCharacter = reinterpret_cast(pParentObject); + + if (pCharacter != NULL) + { + return pCharacter->GetEmotionIds(); + } + else + { + return QStringList(); + } +} + +template <> +QStringList CaseContent::GetDisplayNames(void *pParentObject) +{ + Character *pCharacter = reinterpret_cast(pParentObject); + + if (pCharacter != NULL) + { + return pCharacter->GetEmotionIds(); + } + else + { + return QStringList(); + } +} + +template <> +void CaseContent::Add(QString /*id*/, Character::FieldSprite * /*pDialogEmotion*/, void * /*pParentObject*/) +{ + throw new MLIException("There is always a set number of field sprites on a character - we should never be adding a new one."); +} + +template <> +Character::FieldSprite * CaseContent::GetById(QString id, void *pParentObject) +{ + Character *pCharacter = reinterpret_cast(pParentObject); + + if (pCharacter != NULL) + { + if (id == pCharacter->GetStandingSprite()->GetId()) + { + return pCharacter->GetStandingSprite(); + } + else if (id == pCharacter->GetWalkingSprite()->GetId()) + { + return pCharacter->GetWalkingSprite(); + } + else if (id == pCharacter->GetRunningSprite()->GetId()) + { + return pCharacter->GetRunningSprite(); + } + else + { + throw new MLIException("Unknown field sprite ID."); + } + } + else + { + return NULL; + } +} + +template <> +QStringList CaseContent::GetIds(void *pParentObject) +{ + Character *pCharacter = reinterpret_cast(pParentObject); + + if (pCharacter != NULL) + { + QStringList ids; + + ids.append(pCharacter->GetStandingSprite()->GetId()); + ids.append(pCharacter->GetWalkingSprite()->GetId()); + ids.append(pCharacter->GetRunningSprite()->GetId()); + + return ids; + } + else + { + return QStringList(); + } +} + +template <> +QStringList CaseContent::GetDisplayNames(void *pParentObject) +{ + return GetIds(pParentObject); +} diff --git a/src/CaseCreator/CaseContent/Character.h b/src/CaseCreator/CaseContent/Character.h new file mode 100644 index 0000000..90cf31f --- /dev/null +++ b/src/CaseCreator/CaseContent/Character.h @@ -0,0 +1,534 @@ +#ifndef CHARACTER_H +#define CHARACTER_H + +#include +#include +#include +#include + +#include "Animation.h" +#include "Condition.h" +#include "HitBox.h" +#include "enums.h" + +#include "CaseCreator/Utilities/Interfaces.h" +#include "CaseCreator/UIComponents/ManipulationViewBox.h" + +#include "XmlStorableObject.h" + +class ObjectManipulatorSlots; + +namespace Staging +{ + class DialogCharacter; + class FieldCharacter; +} + +class Character : public XmlStorableObject +{ + BEGIN_XML_STORABLE_OBJECT(Character) + XML_STORABLE_TEXT(id) + XML_STORABLE_TEXT(name) + XML_STORABLE_TEXT(defaultEmotionId) + XML_STORABLE_CUSTOM_OBJECT(pHitBox, HitBox::CreateFromXml) + XML_STORABLE_INT(anchorPosition) + XML_STORABLE_CUSTOM_OBJECT(clickRect, RectangleWH::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT_MAP(emotionByIdMap, DialogEmotion::CreateFromXml) + END_XML_STORABLE_OBJECT() + +public: + Character(); + Character(QString id, QString name, QString defaultEmotionId); + Character(Staging::DialogCharacter *pStagingDialogCharacter, Staging::FieldCharacter *pStagingFieldCharacter); + virtual ~Character(); + + static Character * CreateFromXml(XmlReader *pReader) + { + return new Character(pReader); + } + + void SaveElementsToXml(XmlWriter *pWriter); + void LoadElementsFromXml(XmlReader *pReader); + + static QString GetObjectAdditionString() { return QString("character"); } + static QString GetListTitle() { return QString("Characters"); } + static bool GetIsMainList() { return true; } + static bool GetAllowsNewObjects() { return true; } + + QString GetId() const { return this->id; } + + QString GetName() const { return this->name; } + void SetName(QString name) { this->name = name; } + + QString GetDisplayName() const { return this->name.length() > 0 ? this->name : this->id; } + + //// DIALOG CHARACTER PORTION //// + QString GetDefaultEmotionId() const { return this->defaultEmotionId; } + const QStringList& GetEmotionIds() { return this->emotionIdList; } + + class DialogEmotion : public XmlStorableObject + { + BEGIN_XML_STORABLE_OBJECT(DialogEmotion) + XML_STORABLE_TEXT(id) + XML_STORABLE_FILE_PATH(baseFilePath) + XML_STORABLE_FILE_PATH_LIST(eyeFilePathList) + XML_STORABLE_FILE_PATH_LIST(mouthFilePathList) + END_XML_STORABLE_OBJECT() + + public: + DialogEmotion() + { + lastMouthIndex = -1; + } + + DialogEmotion(QString id) + : DialogEmotion() + { + this->id = id; + } + + virtual ~DialogEmotion() { } + + static DialogEmotion * CreateFromXml(XmlReader *pReader) + { + return new DialogEmotion(pReader); + } + + static QString GetObjectAdditionString() { return QString("dialog sprite"); } + static QString GetListTitle() { return QString("Dialog Sprites"); } + static bool GetIsMainList() { return false; } + static bool GetAllowsNewObjects() { return true; } + + QString GetId() const { return this->id; } + void SetId(const QString &id) { this->id = id; } + + QString GetBaseFilePath(); + QString GetEyeFilePath(int eyeIndex); + QStringList GetEyeFilePaths(); + QString GetMouthFilePath(int mouthIndex); + QString GetRandomMouthFilePath(); + QStringList GetMouthFilePaths(); + + int GetEyeDuration(int eyeIndex); + int GetEyeFrameCount(); + int GetMouthFrameCount(); + + void SetBaseFilePath(QString filePath); + + void AddEyeFrame(QString filePath); + void SetEyeFrame(int index, QString filePath); + void SetEyeFrames(QStringList filePaths); + void InsertEyeFrame(int index, QString filePath); + void RemoveEyeFrame(int index); + + void AddMouthFrame(QString filePath); + void SetMouthFrame(int index, QString filePath); + void SetMouthFrames(QStringList filePaths); + void InsertMouthFrame(int index, QString filePath); + void RemoveMouthFrame(int index); + + class DrawingView : public IDrawingView + { + public: + DrawingView(Character::DialogEmotion *pEmotion, IDrawable *pParent); + ~DrawingView(); + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void DrawChildren(QGraphicsScene *pScene); + void UpdateCore() { } + void UpdateChildren(); + void ResetChildren(); + + void SetIsSpeaking(bool isSpeaking); + + private: + class EyeDrawable : public IDrawable + { + public: + EyeDrawable(Character::DialogEmotion *pEmotion, IDrawable *pParent); + ~EyeDrawable() { } + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void UpdateCore(); + void UpdateOnTimeout(); + + qreal GetZOrder() + { + return 1; + } + + private: + void UpdateTimeout(); + + Character::DialogEmotion *pEmotion; + + int currentEyeIndex; + QList eyePixmapItems; + }; + + class MouthDrawable : public IDrawable + { + public: + MouthDrawable(Character::DialogEmotion *pEmotion, IDrawable *pParent); + ~MouthDrawable() { } + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void UpdateCore(); + void UpdateOnTimeout(); + + qreal GetZOrder() + { + // The mouth should be on top of the eyes, so let's make sure of that + // by giving it a larger z-order. + return 2; + } + + void SetIsSpeaking(bool isSpeaking); + + private: + Character::DialogEmotion *pEmotion; + + int currentMouthIndex; + QList mouthPixmapItems; + + bool isSpeaking; + }; + + QString bodyBaseFilePath; + QGraphicsPixmapItem *pBodyBasePixmapItem; + EyeDrawable *pEyeDrawable; + MouthDrawable *pMouthDrawable; + }; + + GET_DRAWING_VIEW_DEFINITION(Character::DialogEmotion) + + private: + QString id; + + QString baseFilePath; + QStringList eyeFilePathList; + QStringList mouthFilePathList; + + int lastMouthIndex; + }; + + void AddDialogEmotion(QString emotionId); + DialogEmotion * GetDialogEmotion(QString emotionId); + void SetDialogEmotion(QString emotionId, DialogEmotion *pDialogEmotion); + + //// FIELD CHARACTER PORTION //// + Animation * GetStandingAnimationForDirection(FieldCharacterDirection direction); + Animation * GetWalkingAnimationForDirection(FieldCharacterDirection direction); + Animation * GetRunningAnimationForDirection(FieldCharacterDirection direction); + + HitBox * GetHitBox() const { return pHitBox; } + int GetAnchorPosition() const { return anchorPosition; } + RectangleWH GetClickRect() const { return clickRect; } + + // This is a utility class used to expose the field animations in such a way that can be + // enumerated in a list. + class FieldSprite + { + public: + FieldSprite() {} + + QString GetId() const { return this->id; } + void SetId(const QString &id) { this->id = id; } + + static QString GetObjectAdditionString() { return QString("field sprite"); } + static QString GetListTitle() { return QString("Field Sprites"); } + static bool GetIsMainList() { return false; } + static bool GetAllowsNewObjects() { return false; } + + Animation * GetAnimationForDirection(FieldCharacterDirection direction) + { + return ppAnimationsOnCharacter[direction] != NULL ? *ppAnimationsOnCharacter[direction] : NULL; + } + + void SetAnimationForDirection(FieldCharacterDirection direction, Animation **ppAnimationOnCharacter) + { + ppAnimationsOnCharacter[direction] = ppAnimationOnCharacter; + } + + private: + QString id; + Animation **ppAnimationsOnCharacter[FieldCharacterDirectionCount]; + }; + + FieldSprite * GetStandingSprite() { return &standingSprite; } + FieldSprite * GetWalkingSprite() { return &walkingSprite; } + FieldSprite * GetRunningSprite() { return &runningSprite; } + + class FieldInstance : public IManipulatableObject, public XmlStorableObject + { + BEGIN_NAMED_XML_STORABLE_OBJECT(FieldInstance, CharacterFieldInstance) + XML_STORABLE_CUSTOM_OBJECT(position, Vector2::CreateFromXml) + XML_STORABLE_ENUM(direction, CharacterDirectionToString, StringToCharacterDirection) + XML_STORABLE_ENUM(spriteDirection, FieldCharacterDirectionToString, StringToFieldCharacterDirection) + XML_STORABLE_TEXT(clickEncounterId) + XML_STORABLE_TEXT(clickCutsceneId) + XML_STORABLE_CUSTOM_OBJECT(interactionLocation, Vector2::CreateFromXml) + XML_STORABLE_BOOL(interactFromAnywhere) + XML_STORABLE_CUSTOM_OBJECT(pCondition, Condition::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + FieldInstance() + { + pCharacter = NULL; + direction = CharacterDirectionNone; + spriteDirection = FieldCharacterDirectionNone; + } + + FieldInstance(Staging::FieldCharacter *pStagingFieldCharacter); + virtual ~FieldInstance(); + + static Character::FieldInstance * CreateFromXml(XmlReader *pReader) + { + return new Character::FieldInstance(pReader); + } + + void WriteToCaseXml(XmlWriter *pWriter); + + QString GetId(); + + QString GetName(); + void SetName(QString name); + + QString GetDisplayName(); + + Vector2 GetPosition() const { return position; } + void SetPosition(Vector2 position); + + Vector2 GetMidpoint() const; + Vector2 GetMidpointForPosition(const Vector2 &position) const; + + CharacterDirection GetDirection() const { return direction; } + FieldCharacterDirection GetSpriteDirection() const { return spriteDirection; } + QString GetClickEncounterId() const { return clickEncounterId; } + QString GetClickCutsceneId() const { return clickCutsceneId; } + Vector2 GetInteractionLocation() const { return interactionLocation; } + bool GetInteractFromAnywhere() const { return interactFromAnywhere; } + Condition * GetCondition() const { return pCondition; } + + void SaveElementsToXml(XmlWriter *pWriter); + void LoadElementsFromXml(XmlReader *pReader); + + virtual ObjectManipulatorSlots * GetManipulationWidget(); + + class ManipulationView : public IManipulationView + { + public: + ManipulationView(Character::FieldInstance *pCharacterFieldInstance, IDrawable *pParent, UndoCommandSink *pUndoCommandSink); + ~ManipulationView(); + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void DrawChildren(QGraphicsScene *pScene); + void UpdateChildren(); + void ResetChildren(); + + virtual QString GetMouseOverText() + { + return pObject->GetName(); + } + + Vector2 GetPosition() + { + if (positionIsOverridden) + { + return positionOverride; + } + else + { + return pObject->position; + } + } + + RectangleWH GetBoundingRect(); + qreal GetZOrder(); + + qreal GetOpacity() + { + if (IsEnabled()) + { + return pParent->GetOpacity() * (pManipulationViewBox->GetIsBeingDragged() ? 0.5 : 1.0); + } + else + { + return IManipulatable::GetOpacity(); + } + } + + bool GetFlipHorizontal() + { + if (directionIsOverridden) + { + return directionOverride == CharacterDirectionRight; + } + else + { + return pObject->GetDirection() == CharacterDirectionRight; + } + } + + bool GetShouldDimIfDisabled() + { + return shouldDimIfDisabled; + } + + void SetShouldDimIfDisabled(bool shouldDimIfDisabled); + + void SetPositionOverride(Vector2 position); + void ClearPositionOverride(); + void SetDirectionOverride(CharacterDirection direction); + void ClearDirectionOverride(); + void SetSpriteDirectionOverride(FieldCharacterDirection direction); + void ClearSpriteDirectionOverride(); + + protected: + void OnGotFocus(); + void OnLostFocus(); + + void OnMouseEnter(); + void OnMouseLeave(); + + void OnMousePress(); + void OnMouseDrag(Vector2 delta); + void OnMouseRelease(); + + private: + void SetPosition(Vector2 position); + void SetIsMouseOver(bool isMouseOver); + void SetIsSelected(bool isSelected); + void SetIsBeingDragged(bool isBeingDragged); + + void UpdateAnimationDrawingView(); + + Animation::DrawingView *pSpriteAnimationDrawingViews[FieldCharacterDirectionCount]; + ManipulationViewBox *pManipulationViewBox; + + bool shouldDimIfDisabled; + + Vector2 positionBeforeDrag; + + bool positionIsOverridden; + bool directionIsOverridden; + bool spriteDirectionIsOverridden; + + Vector2 positionOverride; + CharacterDirection directionOverride; + FieldCharacterDirection spriteDirectionOverride; + }; + + GET_MANIPULATION_VIEW_DEFINITION(Character::FieldInstance) + + private: + Character *pCharacter; + + Vector2 position; + CharacterDirection direction; + FieldCharacterDirection spriteDirection; + QString clickEncounterId; + QString clickCutsceneId; + Vector2 interactionLocation; + bool interactFromAnywhere; + Condition *pCondition; + }; + + class DialogDrawingView : public IDrawingView + { + public: + DialogDrawingView(Character *pCharacter, IDrawable *pParent); + ~DialogDrawingView(); + + void DrawCore(QGraphicsScene * /*pScene */, QList & /*addedItems*/) { } + void DrawChildren(QGraphicsScene *pScene); + void UpdateCore() { } + void UpdateChildren(); + void ResetChildren(); + + Vector2 GetPosition() + { + return position; + } + + void SetPosition(Vector2 position) + { + this->position = position; + Update(); + } + + bool GetFlipHorizontal() + { + return isFlipped; + } + + void SetIsFlipped(bool isFlipped) + { + this->isFlipped = isFlipped; + Update(); + } + + void SetEmotion(QString emotionId); + void SetIsSpeaking(bool isSpeaking); + + private: + Vector2 position; + bool isFlipped; + + QString currentEmotionId; + Character::DialogEmotion::DrawingView *pEmotionDrawingView; + }; + + Character::DialogDrawingView * GetDialogDrawingView(IDrawable *pParent) + { + return new Character::DialogDrawingView(this, pParent); + } + +private: + QString id; + QString name; + + //// DIALOG CHARACTER PORTION //// + QString defaultEmotionId; + QStringList emotionIdList; + + QMap emotionByIdMap; + + //// FIELD CHARACTER PORTION //// + Animation * standingAnimations[FieldCharacterDirectionCount]; + Animation * walkingAnimations[FieldCharacterDirectionCount]; + Animation * runningAnimations[FieldCharacterDirectionCount]; + + FieldSprite standingSprite; + FieldSprite walkingSprite; + FieldSprite runningSprite; + + HitBox *pHitBox; + int anchorPosition; + RectangleWH clickRect; +}; + +class CharacterSelector : public QComboBox +{ + Q_OBJECT + +public: + explicit CharacterSelector(bool allowNoCharacter, QWidget *parent = 0); + + QString GetSelectedCharacterId(); + void SetToCharacterById(const QString &id); + +signals: + void CharacterSelected(const QString &characterDisplayName); + +public slots: + void CurrentIndexChanged(int); + +private: + QStringList characterIds; + QStringList characterDisplayNames; + bool allowNoCharacter; +}; + +#endif // CHARACTER_H diff --git a/src/CaseCreator/CaseContent/Condition.cpp b/src/CaseCreator/CaseContent/Condition.cpp new file mode 100644 index 0000000..be14e14 --- /dev/null +++ b/src/CaseCreator/CaseContent/Condition.cpp @@ -0,0 +1,826 @@ +#include "Condition.h" + +#include "CaseCreator/CaseContent/Staging/Condition.Staging.h" + +#include "Character.h" +#include "Encounter.h" +#include "CaseCreator/CaseContent/CaseContent.h" +#include "CaseCreator/Utilities/Utilities.h" + +#include +#include + +Condition::Condition(Staging::Condition *pStagingCondition) +{ + if (pStagingCondition != NULL && pStagingCondition->pCriterion != NULL) + { + pCriterion = ConditionCriterion::LoadFromStagingCriterion(pStagingCondition->pCriterion); + } + else + { + pCriterion = NULL; + } +} + +Condition::~Condition() +{ + delete pCriterion; + pCriterion = NULL; +} + +QString Condition::ToString() +{ + return pCriterion->ToString(); +} + +ConditionEditor * Condition::GetEditor() +{ + ConditionEditor *pConditionEditor = new ConditionEditor(); + pConditionEditor->SetCriterionEditor(pCriterion->GetEditor()); + return pConditionEditor; +} + +Condition * Condition::Clone() +{ + Condition *pCondition = new Condition(); + + if (pCriterion != NULL) + { + pCondition->pCriterion = this->pCriterion->Clone(); + } + + return pCondition; +} + +Condition::ConditionCriterion * Condition::ConditionCriterion::CreateFromXml(XmlReader *pReader) +{ + if (pReader->ElementExists("FlagSetCondition")) + { + return new Condition::FlagSetCondition(pReader); + } + else if (pReader->ElementExists("EvidencePresentCondition")) + { + return new Condition::EvidencePresentCondition(pReader); + } + else if (pReader->ElementExists("PartnerPresentCondition")) + { + return new Condition::PartnerPresentCondition(pReader); + } + else if (pReader->ElementExists("ConversationLockedCondition")) + { + return new Condition::ConversationLockedCondition(pReader); + } + else if (pReader->ElementExists("TutorialsEnabledCondition")) + { + return new Condition::TutorialsEnabledCondition(pReader); + } + else if (pReader->ElementExists("AndCondition")) + { + return new Condition::AndCondition(pReader); + } + else if (pReader->ElementExists("OrCondition")) + { + return new Condition::OrCondition(pReader); + } + else if (pReader->ElementExists("NotCondition")) + { + return new Condition::NotCondition(pReader); + } + else + { + throw MLIException("Invalid criterion type."); + } +} + +Condition::ConditionCriterion * Condition::ConditionCriterion::LoadFromStagingCriterion(Staging::Condition::ConditionCriterion *pStagingConditionCriterion) +{ + switch (pStagingConditionCriterion->GetType()) + { + case Staging::ConditionCriterionType_FlagSet: + { + return new Condition::FlagSetCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_EvidencePresent: + { + return new Condition::EvidencePresentCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_PartnerPresent: + { + return new Condition::PartnerPresentCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_ConversationLocked: + { + return new Condition::ConversationLockedCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_TutorialsEnabled: + { + return new Condition::TutorialsEnabledCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_And: + { + return new Condition::AndCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_Or: + { + return new Condition::OrCondition(static_cast(pStagingConditionCriterion)); + } + + case Staging::ConditionCriterionType_Not: + { + // We only want negation to be occurring at the lowest level, so if we're negating an AND or an OR, + // we'll get rid of that by propagating the negation down to the lower levels and flipping the Boolean operator. + Condition::ConditionCriterion *pReturnCondition = NULL; + Staging::Condition::NotCondition *pNotCondition = static_cast(pStagingConditionCriterion); + + if (pNotCondition->pCriterion->GetType() == Staging::ConditionCriterionType_And) + { + Staging::Condition::AndCondition *pChildAndCondition = static_cast(pNotCondition->pCriterion); + + Staging::Condition::NotCondition *pNegatedFirstCondition = new Staging::Condition::NotCondition(pChildAndCondition->pFirstCriterion); + Staging::Condition::NotCondition *pNegatedSecondCondition = new Staging::Condition::NotCondition(pChildAndCondition->pSecondCriterion); + + Staging::Condition::OrCondition *pEquivalentOrCondition = new Staging::Condition::OrCondition(pNegatedFirstCondition, pNegatedSecondCondition); + + pChildAndCondition->pFirstCriterion = NULL; + pChildAndCondition->pSecondCriterion = NULL; + + pReturnCondition = new Condition::OrCondition(pEquivalentOrCondition); + + delete pEquivalentOrCondition; + } + else if (pNotCondition->pCriterion->GetType() == Staging::ConditionCriterionType_Or) + { + Staging::Condition::OrCondition *pChildOrCondition = static_cast(pNotCondition->pCriterion); + + Staging::Condition::NotCondition *pNegatedFirstCondition = new Staging::Condition::NotCondition(pChildOrCondition->pFirstCriterion); + Staging::Condition::NotCondition *pNegatedSecondCondition = new Staging::Condition::NotCondition(pChildOrCondition->pSecondCriterion); + + Staging::Condition::AndCondition *pEquivalentAndCondition = new Staging::Condition::AndCondition(pNegatedFirstCondition, pNegatedSecondCondition); + + pChildOrCondition->pFirstCriterion = NULL; + pChildOrCondition->pSecondCriterion = NULL; + + pReturnCondition = new Condition::AndCondition(pEquivalentAndCondition); + + delete pEquivalentAndCondition; + } + else + { + pReturnCondition = new Condition::NotCondition(static_cast(pStagingConditionCriterion)); + } + + return pReturnCondition; + } + + default: + throw MLIException("Invalid criterion type."); + } +} + +Condition::FlagSetCondition::FlagSetCondition(const QString &flagId) + : flagId(flagId) +{ +} + +Condition::FlagSetCondition::FlagSetCondition(Staging::Condition::FlagSetCondition *pStagingFlagSetCondition) +{ + flagId = pStagingFlagSetCondition->FlagId; +} + +Condition::FlagSetCondition::~FlagSetCondition() +{ +} + +CriterionEditor * Condition::FlagSetCondition::GetEditor() +{ + SingleCriterionEditor *pSingleCriterionEditor = new SingleCriterionEditor(); + pSingleCriterionEditor->SetCriterion(SingleCriterionType::FlagSet, flagId); + return pSingleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::FlagSetCondition::Clone() +{ + return new FlagSetCondition(flagId); +} + +Condition::EvidencePresentCondition::EvidencePresentCondition(const QString &evidenceId) + : evidenceId(evidenceId) +{ +} + +Condition::EvidencePresentCondition::EvidencePresentCondition(Staging::Condition::EvidencePresentCondition *pStagingEvidencePresentCondition) +{ + evidenceId = pStagingEvidencePresentCondition->EvidenceId; +} + +Condition::EvidencePresentCondition::~EvidencePresentCondition() +{ + +} + +CriterionEditor * Condition::EvidencePresentCondition::GetEditor() +{ + SingleCriterionEditor *pSingleCriterionEditor = new SingleCriterionEditor(); + pSingleCriterionEditor->SetCriterion(SingleCriterionType::EvidencePresent, evidenceId); + return pSingleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::EvidencePresentCondition::Clone() +{ + return new EvidencePresentCondition(evidenceId); +} + +Condition::PartnerPresentCondition::PartnerPresentCondition(const QString &partnerId) + : partnerId(partnerId) +{ +} + +Condition::PartnerPresentCondition::PartnerPresentCondition(Staging::Condition::PartnerPresentCondition *pStagingPartnerPresentCondition) +{ + partnerId = pStagingPartnerPresentCondition->PartnerId; +} + +Condition::PartnerPresentCondition::~PartnerPresentCondition() +{ +} + +CriterionEditor * Condition::PartnerPresentCondition::GetEditor() +{ + SingleCriterionEditor *pSingleCriterionEditor = new SingleCriterionEditor(); + pSingleCriterionEditor->SetCriterion(SingleCriterionType::PartnerPresent, partnerId); + return pSingleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::PartnerPresentCondition::Clone() +{ + return new PartnerPresentCondition(partnerId); +} + +Condition::ConversationLockedCondition::ConversationLockedCondition(const QString &encounterId, const QString &conversationId) + : encounterId(encounterId) + , conversationId(conversationId) +{ +} + +Condition::ConversationLockedCondition::ConversationLockedCondition(Staging::Condition::ConversationLockedCondition *pStagingConversationLockedCondition) +{ + SplitConversationIdFromCaseFile(pStagingConversationLockedCondition->ConversationId, &encounterId, &conversationId); +} + +Condition::ConversationLockedCondition::~ConversationLockedCondition() +{ +} + +CriterionEditor * Condition::ConversationLockedCondition::GetEditor() +{ + SingleCriterionEditor *pSingleCriterionEditor = new SingleCriterionEditor(); + pSingleCriterionEditor->SetCriterion(SingleCriterionType::ConversationLocked, conversationId); + return pSingleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::ConversationLockedCondition::Clone() +{ + return new ConversationLockedCondition(encounterId, conversationId); +} + +Condition::TutorialsEnabledCondition::TutorialsEnabledCondition(Staging::Condition::TutorialsEnabledCondition */*pStagingTutorialsEnabledCondition*/) +{ +} + +Condition::TutorialsEnabledCondition::~TutorialsEnabledCondition() +{ +} + +CriterionEditor * Condition::TutorialsEnabledCondition::GetEditor() +{ + SingleCriterionEditor *pSingleCriterionEditor = new SingleCriterionEditor(); + pSingleCriterionEditor->SetCriterion(SingleCriterionType::TutorialsEnabled); + return pSingleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::TutorialsEnabledCondition::Clone() +{ + return new TutorialsEnabledCondition(); +} + +Condition::AndCondition::AndCondition(Condition::ConditionCriterion *pFirstCriterion, Condition::ConditionCriterion *pSecondCriterion) + : AndCondition() +{ + this->pFirstCriterion = pFirstCriterion; + this->pSecondCriterion = pSecondCriterion; +} + +Condition::AndCondition::AndCondition(Staging::Condition::AndCondition *pStagingAndCondition) + : AndCondition() +{ + pFirstCriterion = ConditionCriterion::LoadFromStagingCriterion(pStagingAndCondition->pFirstCriterion); + pSecondCriterion = ConditionCriterion::LoadFromStagingCriterion(pStagingAndCondition->pSecondCriterion); +} + +Condition::AndCondition::~AndCondition() +{ + delete pFirstCriterion; + pFirstCriterion = NULL; + delete pSecondCriterion; + pSecondCriterion = NULL; +} + +CriterionEditor * Condition::AndCondition::GetEditor() +{ + MultipleCriterionEditor *pMultipleCriterionEditor = new MultipleCriterionEditor(); + pMultipleCriterionEditor->SetCriterions(MultipleCriterionType::And, pFirstCriterion, pSecondCriterion); + return pMultipleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::AndCondition::Clone() +{ + return new AndCondition(pFirstCriterion->Clone(), pSecondCriterion->Clone()); +} + +Condition::OrCondition::OrCondition(Condition::ConditionCriterion *pFirstCriterion, Condition::ConditionCriterion *pSecondCriterion) + : OrCondition() +{ + this->pFirstCriterion = pFirstCriterion; + this->pSecondCriterion = pSecondCriterion; +} + +Condition::OrCondition::OrCondition(Staging::Condition::OrCondition *pStagingOrCondition) + : OrCondition() +{ + pFirstCriterion = ConditionCriterion::LoadFromStagingCriterion(pStagingOrCondition->pFirstCriterion); + pSecondCriterion = ConditionCriterion::LoadFromStagingCriterion(pStagingOrCondition->pSecondCriterion); +} + +Condition::OrCondition::~OrCondition() +{ + delete pFirstCriterion; + pFirstCriterion = NULL; + delete pSecondCriterion; + pSecondCriterion = NULL; +} + +CriterionEditor * Condition::OrCondition::GetEditor() +{ + MultipleCriterionEditor *pMultipleCriterionEditor = new MultipleCriterionEditor(); + pMultipleCriterionEditor->SetCriterions(MultipleCriterionType::Or, pFirstCriterion, pSecondCriterion); + return pMultipleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::OrCondition::Clone() +{ + return new OrCondition(pFirstCriterion->Clone(), pSecondCriterion->Clone()); +} + +Condition::NotCondition::NotCondition(Condition::ConditionCriterion *pCriterion) + : NotCondition() +{ + this->pCriterion = pCriterion; +} + +Condition::NotCondition::NotCondition(Staging::Condition::NotCondition *pStagingNotCondition) + : NotCondition() +{ + pCriterion = ConditionCriterion::LoadFromStagingCriterion(pStagingNotCondition->pCriterion); +} + +Condition::NotCondition::~NotCondition() +{ + delete pCriterion; + pCriterion = NULL; +} + +CriterionEditor * Condition::NotCondition::GetEditor() +{ + CriterionEditor *pChildCriterionEditor = pCriterion->GetEditor(); + SingleCriterionEditor *pChildSingleCriterionEditor = dynamic_cast(pChildCriterionEditor); + + if (pChildSingleCriterionEditor == NULL) + { + qFatal("NotConditions should always only modify leaf elements in a condition tree."); + } + + pChildSingleCriterionEditor->SetIsNegated(true); + return pChildSingleCriterionEditor; +} + +Condition::ConditionCriterion * Condition::NotCondition::Clone() +{ + return new NotCondition(pCriterion->Clone()); +} + +ConditionEditor::ConditionEditor() + : QWidget() +{ + pMainLayout = new QVBoxLayout(); + pMainLayout->setSpacing(0); + pMainLayout->setContentsMargins(0, 0, 0, 0); + setLayout(pMainLayout); + + pCriterionEditor = NULL; + + QStringList flagIds = CaseContent::GetInstance()->GetIds(); + + SingleCriterionEditor *pInitialCriterionEditor = new SingleCriterionEditor(); + pInitialCriterionEditor->SetCriterion(SingleCriterionType::FlagSet, flagIds.length() > 0 ? flagIds[0] : ""); + + SetCriterionEditor(pInitialCriterionEditor); +} + +void ConditionEditor::SetCriterionEditor(CriterionEditor *pCriterionEditor) +{ + if (this->pCriterionEditor != NULL) + { + pMainLayout->removeWidget(this->pCriterionEditor); + delete this->pCriterionEditor; + } + + this->pCriterionEditor = pCriterionEditor; + + if (this->pCriterionEditor != NULL) + { + pMainLayout->addWidget(this->pCriterionEditor); + } +} + +Condition * ConditionEditor::GetCondition() +{ + Condition *pCondition = new Condition(); + + pCondition->pCriterion = pCriterionEditor->GetCriterion(); + + return pCondition; +} + +CriterionEditor::CriterionEditor() + : QWidget() +{ +} + +SingleCriterionEditor::SingleCriterionEditor() + : CriterionEditor() +{ + criterionTypeSelected = SingleCriterionType::FlagSet; + pFlagSelected = NULL; + pEvidenceSelected = NULL; + pCharacterSelected = NULL; + pEncounterSelected = NULL; + pConversationSelected = NULL; + + QHBoxLayout *pMainLayout = new QHBoxLayout(); + + pNotCheckBox = new QCheckBox(); + pNotCheckBox->setChecked(false); + pNotCheckBox->setText("Not"); + pMainLayout->addWidget(pNotCheckBox); + + pCriterionTypeComboBox = new QComboBox(); + pCriterionTypeComboBox->addItem("Flag is set..."); + pCriterionTypeComboBox->addItem("Evidence is present..."); + pCriterionTypeComboBox->addItem("Partner is present..."); + pCriterionTypeComboBox->addItem("Conversation is locked..."); + pCriterionTypeComboBox->addItem("Tutorials are enabled"); + pCriterionTypeComboBox->setCurrentIndex(0); + QObject::connect(pCriterionTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(CriterionTypeComboBoxCurrentIndexChanged(int))); + pMainLayout->addWidget(pCriterionTypeComboBox); + + pFlagEditor = new FlagEditor(); + QObject::connect(pFlagEditor, SIGNAL(FlagSelected(QString)), this, SLOT(FlagEditorFlagSelected(QString))); + pMainLayout->addWidget(pFlagEditor); + + pEvidenceSelector = new EvidenceSelector(); + QObject::connect(pEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(EvidenceSelectorEvidenceSelected(QString))); + pMainLayout->addWidget(pEvidenceSelector); + + pPartnerSelector = new CharacterSelector(false /* allowNoCharacter */); + QObject::connect(pPartnerSelector, SIGNAL(CharacterSelected(QString)), this, SLOT(PartnerSelectorCharacterSelected(QString))); + pMainLayout->addWidget(pPartnerSelector); + + pConversationLockedEncounterSelector = new EncounterSelector(); + QObject::connect(pConversationLockedEncounterSelector, SIGNAL(EncounterSelected(QString)), this, SLOT(ConversationLockedEncounterSelectorEncounterSelected(QString))); + pMainLayout->addWidget(pConversationLockedEncounterSelector); + + pConversationLockedSelector = new ConversationSelector(); + QObject::connect(pConversationLockedSelector, SIGNAL(ConversationSelected(QString)), this, SLOT(ConversationLockedConversationSelectorConversationSelected(QString))); + pMainLayout->addWidget(pConversationLockedSelector); + + pMainLayout->addStretch(1); + pMainLayout->setContentsMargins(0, 0, 0, 0); + setLayout(pMainLayout); + + // Call these explicitly to make sure we're initialized properly. + CriterionTypeComboBoxCurrentIndexChanged(0); + FlagEditorFlagSelected(pFlagEditor->itemText(0)); +} + +void SingleCriterionEditor::SetCriterion(SingleCriterionType criterionType, const QString &criterionId) +{ + criterionTypeSelected = criterionType; + pCriterionTypeComboBox->setCurrentIndex((int)criterionTypeSelected); + + switch (criterionType) + { + case SingleCriterionType::FlagSet: + pFlagEditor->setCurrentText(criterionId); + FlagEditorFlagSelected(criterionId); + break; + + case SingleCriterionType::EvidencePresent: + pEvidenceSelector->SetToId(criterionId); + EvidenceSelectorEvidenceSelected(criterionId); + break; + + case SingleCriterionType::PartnerPresent: + pPartnerSelector->SetToCharacterById(criterionId); + PartnerSelectorCharacterSelected(criterionId); + break; + + case SingleCriterionType::ConversationLocked: + { + QString encounterIdSelected; + + for (const QString &encounterId : CaseContent::GetInstance()->GetIds()) + { + Encounter *pEncounter = CaseContent::GetInstance()->GetById(encounterId); + + if (CaseContent::GetInstance()->GetIds(pEncounter).contains(criterionId)) + { + encounterIdSelected = encounterId; + break; + } + } + + if (encounterIdSelected.length() > 0) + { + pConversationLockedEncounterSelector->SetToId(encounterIdSelected); + ConversationLockedEncounterSelectorEncounterSelected(encounterIdSelected); + pConversationLockedSelector->SetToId(criterionId); + ConversationLockedConversationSelectorConversationSelected(criterionId); + } + + break; + } + + case SingleCriterionType::TutorialsEnabled: + break; + + default: + qFatal("Invalid single criterion type selected."); + } +} + +void SingleCriterionEditor::SetIsNegated(bool isNegated) +{ + pNotCheckBox->setChecked(isNegated); +} + +Condition::ConditionCriterion * SingleCriterionEditor::GetCriterion() +{ + Condition::ConditionCriterion *pCriterion = NULL; + + switch (criterionTypeSelected) + { + case SingleCriterionType::FlagSet: + pCriterion = new Condition::FlagSetCondition(pFlagSelected->GetId()); + break; + + case SingleCriterionType::EvidencePresent: + pCriterion = new Condition::EvidencePresentCondition(pEvidenceSelected->GetId()); + break; + + case SingleCriterionType::PartnerPresent: + pCriterion = new Condition::PartnerPresentCondition(pCharacterSelected->GetId()); + break; + + case SingleCriterionType::ConversationLocked: + pCriterion = new Condition::ConversationLockedCondition(pEncounterSelected->GetId(), pConversationSelected->GetId()); + break; + + case SingleCriterionType::TutorialsEnabled: + pCriterion = new Condition::TutorialsEnabledCondition(); + break; + + default: + qFatal("Invalid single criterion type selected."); + } + + if (pCriterion != NULL && pNotCheckBox->isChecked()) + { + pCriterion = new Condition::NotCondition(pCriterion); + } + + return pCriterion; +} + +void SingleCriterionEditor::CriterionTypeComboBoxCurrentIndexChanged(int newIndex) +{ + criterionTypeSelected = (SingleCriterionType)newIndex; + + switch (criterionTypeSelected) + { + case SingleCriterionType::FlagSet: + if (pFlagSelected != NULL) + { + pFlagEditor->setCurrentText(pFlagSelected->GetId()); + } + else + { + pFlagEditor->setCurrentIndex(0); + } + + pFlagEditor->show(); + + pEvidenceSelector->hide(); + pPartnerSelector->hide(); + pConversationLockedEncounterSelector->hide(); + pConversationLockedSelector->hide(); + + FlagEditorFlagSelected(pFlagEditor->GetId()); + break; + + case SingleCriterionType::EvidencePresent: + if (pEvidenceSelected != NULL) + { + pEvidenceSelector->SetToId(pEvidenceSelected->GetId()); + } + else + { + pEvidenceSelector->SetToId(""); + } + + pEvidenceSelector->show(); + + pFlagEditor->hide(); + pPartnerSelector->hide(); + pConversationLockedEncounterSelector->hide(); + pConversationLockedSelector->hide(); + + EvidenceSelectorEvidenceSelected(pEvidenceSelector->GetId()); + break; + + case SingleCriterionType::PartnerPresent: + if (pCharacterSelected != NULL) + { + pPartnerSelector->SetToCharacterById(pCharacterSelected->GetId()); + } + else + { + pPartnerSelector->SetToCharacterById(""); + } + + pPartnerSelector->show(); + + pFlagEditor->hide(); + pEvidenceSelector->hide(); + pConversationLockedSelector->hide(); + pConversationLockedSelector->hide(); + + PartnerSelectorCharacterSelected(pPartnerSelector->GetSelectedCharacterId()); + break; + + case SingleCriterionType::ConversationLocked: + if (pEncounterSelected != NULL && pConversationSelected != NULL) + { + pConversationLockedEncounterSelector->SetToId(pEncounterSelected->GetId()); + pConversationLockedSelector->SetToId(pConversationSelected->GetId()); + } + else + { + pConversationLockedEncounterSelector->SetToId(""); + pConversationLockedSelector->SetToId(""); + } + + pConversationLockedEncounterSelector->show(); + pConversationLockedSelector->show(); + + pFlagEditor->hide(); + pEvidenceSelector->hide(); + pPartnerSelector->hide(); + + ConversationLockedEncounterSelectorEncounterSelected(pConversationLockedEncounterSelector->GetId()); + ConversationLockedConversationSelectorConversationSelected(pConversationLockedSelector->GetId()); + break; + + case SingleCriterionType::TutorialsEnabled: + pFlagEditor->hide(); + pEvidenceSelector->hide(); + pPartnerSelector->hide(); + pConversationLockedEncounterSelector->hide(); + pConversationLockedSelector->hide(); + break; + + default: + qFatal("Invalid single criterion type selected."); + } +} + +void SingleCriterionEditor::FlagEditorFlagSelected(const QString &flagIdSelected) +{ + pFlagSelected = CaseContent::GetInstance()->GetById(flagIdSelected); +} + +void SingleCriterionEditor::EvidenceSelectorEvidenceSelected(const QString &evidenceIdSelected) +{ + pEvidenceSelected = CaseContent::GetInstance()->GetById(evidenceIdSelected); +} + +void SingleCriterionEditor::PartnerSelectorCharacterSelected(const QString &characterIdSelected) +{ + pCharacterSelected = CaseContent::GetInstance()->GetById(characterIdSelected); +} + +void SingleCriterionEditor::ConversationLockedEncounterSelectorEncounterSelected(const QString &encounterIdSelected) +{ + pEncounterSelected = CaseContent::GetInstance()->GetById(encounterIdSelected); + + if (pEncounterSelected != NULL) + { + pConversationLockedSelector->Reset(pEncounterSelected); + } +} + +void SingleCriterionEditor::ConversationLockedConversationSelectorConversationSelected(const QString &conversationIdSelected) +{ + if (pEncounterSelected != NULL) + { + pConversationSelected = CaseContent::GetInstance()->GetById(conversationIdSelected, pEncounterSelected); + } +} + +MultipleCriterionEditor::MultipleCriterionEditor() +{ + criterionTypeSelected = MultipleCriterionType::And; + pFirstCriterionEditor = NULL; + pSecondCriterionEditor = NULL; + + QGridLayout *pMainLayout = new QGridLayout(); + + pCriterionTypeComboBox = new QComboBox(); + pCriterionTypeComboBox->addItem("And"); + pCriterionTypeComboBox->addItem("Or"); + pCriterionTypeComboBox->setCurrentIndex(0); + QObject::connect(pCriterionTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(CriterionTypeComboBoxCurrentIndexChanged(int))); + pMainLayout->addWidget(pCriterionTypeComboBox, 0, 0, 2, 1); + + pFirstCriterionHolder = new QHBoxLayout(); + pMainLayout->addLayout(pFirstCriterionHolder, 0, 1); + + pSecondCriterionHolder = new QHBoxLayout(); + pMainLayout->addLayout(pSecondCriterionHolder, 1, 1); + + pMainLayout->setContentsMargins(0, 0, 0, 0); + setLayout(pMainLayout); + + CriterionTypeComboBoxCurrentIndexChanged(0); +} + +void MultipleCriterionEditor::SetCriterions(MultipleCriterionType criterionType, Condition::ConditionCriterion *pFirstCriterion, Condition::ConditionCriterion *pSecondCriterion) +{ + criterionTypeSelected = criterionType; + pCriterionTypeComboBox->setCurrentIndex((int)criterionTypeSelected); + + CriterionEditor *pOldFirstCriterionEditor = pFirstCriterionEditor; + CriterionEditor *pOldSecondCriterionEditor = pSecondCriterionEditor; + + pFirstCriterionEditor = pFirstCriterion->GetEditor(); + pSecondCriterionEditor = pSecondCriterion->GetEditor(); + + pFirstCriterionHolder->removeWidget(pOldFirstCriterionEditor); + pFirstCriterionHolder->addWidget(pFirstCriterionEditor); + pSecondCriterionHolder->removeWidget(pOldSecondCriterionEditor); + pSecondCriterionHolder->addWidget(pSecondCriterionEditor); + + delete pOldFirstCriterionEditor; + delete pOldSecondCriterionEditor; +} + +Condition::ConditionCriterion * MultipleCriterionEditor::GetCriterion() +{ + Condition::ConditionCriterion *pCriterion = NULL; + + switch (criterionTypeSelected) + { + case MultipleCriterionType::And: + pCriterion = new Condition::AndCondition(pFirstCriterionEditor->GetCriterion(), pSecondCriterionEditor->GetCriterion()); + break; + + case MultipleCriterionType::Or: + pCriterion = new Condition::OrCondition(pFirstCriterionEditor->GetCriterion(), pSecondCriterionEditor->GetCriterion()); + break; + + default: + qFatal("Invalid multiple criterion type selected."); + } + + return pCriterion; +} + +void MultipleCriterionEditor::CriterionTypeComboBoxCurrentIndexChanged(int newIndex) +{ + criterionTypeSelected = (MultipleCriterionType)newIndex; +} diff --git a/src/CaseCreator/CaseContent/Condition.h b/src/CaseCreator/CaseContent/Condition.h new file mode 100644 index 0000000..5f928b5 --- /dev/null +++ b/src/CaseCreator/CaseContent/Condition.h @@ -0,0 +1,363 @@ +#ifndef CONDITION_H +#define CONDITION_H + +#include "CaseCreator/CaseContent/Staging/Condition.Staging.h" +#include "XmlStorableObject.h" + +#include +#include +#include +#include +#include + +class ConditionEditor; +class CriterionEditor; +class EncounterSelector; + +class Flag; +class FlagEditor; +class Evidence; +class EvidenceSelector; +class Character; +class CharacterSelector; +class Encounter; +class Conversation; +class ConversationSelector; + +class Condition : public XmlStorableObject +{ + friend class ConditionEditor; + + BEGIN_XML_STORABLE_OBJECT(Condition) + XML_STORABLE_CUSTOM_OBJECT(pCriterion, ConditionCriterion::CreateFromXml) + END_XML_STORABLE_OBJECT() + +public: + Condition() + { + pCriterion = NULL; + } + + Condition(Staging::Condition *pStagingCondition); + virtual ~Condition(); + + static Condition * CreateFromXml(XmlReader *pReader) + { + return new Condition(pReader); + } + + QString ToString(); + ConditionEditor * GetEditor(); + + Condition * Clone(); + + class ConditionCriterion : public XmlStorableObject + { + BEGIN_XML_STORABLE_OBJECT(ConditionCriterion) + END_XML_STORABLE_OBJECT() + + public: + ConditionCriterion() { } + virtual ~ConditionCriterion() {} + + virtual QString ToString() = 0; + virtual CriterionEditor * GetEditor() = 0; + + virtual ConditionCriterion * Clone() = 0; + + static ConditionCriterion * CreateFromXml(XmlReader *pReader); + static ConditionCriterion * LoadFromStagingCriterion(Staging::Condition::ConditionCriterion *pStagingConditionCriterion); + }; + + class FlagSetCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(FlagSetCondition, ConditionCriterion) + XML_STORABLE_TEXT(flagId) + END_XML_STORABLE_OBJECT() + + public: + FlagSetCondition() { } + FlagSetCondition(const QString &flagId); + FlagSetCondition(Staging::Condition::FlagSetCondition *pStagingFlagSetCondition); + ~FlagSetCondition(); + + QString ToString() override { return QString("Flag is set - \"") + flagId + "\""; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + QString flagId; + }; + + class EvidencePresentCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(EvidencePresentCondition, ConditionCriterion) + XML_STORABLE_TEXT(evidenceId) + END_XML_STORABLE_OBJECT() + + public: + EvidencePresentCondition() { } + EvidencePresentCondition(const QString &partnerId); + EvidencePresentCondition(Staging::Condition::EvidencePresentCondition *pStagingEvidencePresentCondition); + ~EvidencePresentCondition(); + + QString ToString() override { return QString("Evidence is present - \"") + evidenceId + "\""; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + QString evidenceId; + }; + + class PartnerPresentCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(PartnerPresentCondition, ConditionCriterion) + XML_STORABLE_TEXT(partnerId) + END_XML_STORABLE_OBJECT() + + public: + PartnerPresentCondition() { } + PartnerPresentCondition(const QString &partnerId); + PartnerPresentCondition(Staging::Condition::PartnerPresentCondition *pStagingPartnerPresentCondition); + ~PartnerPresentCondition(); + + QString ToString() override { return QString("Partner is present - \"") + partnerId + "\""; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + QString partnerId; + }; + + class ConversationLockedCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(ConversationLockedCondition, ConditionCriterion) + XML_STORABLE_TEXT(conversationId) + END_XML_STORABLE_OBJECT() + + public: + ConversationLockedCondition() { } + ConversationLockedCondition(const QString &encounterId, const QString &conversationId); + ConversationLockedCondition(Staging::Condition::ConversationLockedCondition *pStagingConversationLockedCondition); + ~ConversationLockedCondition(); + + QString ToString() override { return QString("Conversation is locked - \"") + conversationId + "\""; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + QString encounterId; + QString conversationId; + }; + + class TutorialsEnabledCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(TutorialsEnabledCondition, ConditionCriterion) + END_XML_STORABLE_OBJECT() + + public: + TutorialsEnabledCondition() { } + TutorialsEnabledCondition(Staging::Condition::TutorialsEnabledCondition *pStagingTutorialsEnabledCondition); + ~TutorialsEnabledCondition(); + + QString ToString() override { return "Tutorials are enabled"; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + }; + + class AndCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(AndCondition, ConditionCriterion) + XML_STORABLE_CUSTOM_OBJECT(pFirstCriterion, ConditionCriterion::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT(pSecondCriterion, ConditionCriterion::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + AndCondition() + { + pFirstCriterion = NULL; + pSecondCriterion = NULL; + } + + AndCondition(ConditionCriterion *pFirstCriterion, ConditionCriterion *pSecondCriterion); + AndCondition(Staging::Condition::AndCondition *pStagingAndCondition); + ~AndCondition(); + + QString ToString() override { return QString("(") + pFirstCriterion->ToString() + " AND " + pSecondCriterion->ToString() + ")"; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + ConditionCriterion *pFirstCriterion; + ConditionCriterion *pSecondCriterion; + }; + + class OrCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(OrCondition, ConditionCriterion) + XML_STORABLE_CUSTOM_OBJECT(pFirstCriterion, ConditionCriterion::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT(pSecondCriterion, ConditionCriterion::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + OrCondition() + { + pFirstCriterion = NULL; + pSecondCriterion = NULL; + } + + OrCondition(ConditionCriterion *pFirstCriterion, ConditionCriterion *pSecondCriterion); + OrCondition(Staging::Condition::OrCondition *pStagingOrCondition); + ~OrCondition(); + + QString ToString() override { return QString("(") + pFirstCriterion->ToString() + " OR " + pSecondCriterion->ToString() + ")"; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + ConditionCriterion *pFirstCriterion; + ConditionCriterion *pSecondCriterion; + }; + + class NotCondition : public ConditionCriterion + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(NotCondition, ConditionCriterion) + XML_STORABLE_CUSTOM_OBJECT(pCriterion, ConditionCriterion::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + NotCondition() + { + pCriterion = NULL; + } + + NotCondition(ConditionCriterion *pCriterion); + NotCondition(Staging::Condition::NotCondition *pStagingNotCondition); + ~NotCondition(); + + QString ToString() override { return QString("NOT (") + pCriterion->ToString() + ")"; } + CriterionEditor * GetEditor() override; + + ConditionCriterion * Clone() override; + + private: + ConditionCriterion *pCriterion; + }; + +private: + ConditionCriterion *pCriterion; +}; + +class ConditionEditor : public QWidget +{ + friend class Condition; + + Q_OBJECT + +public: + explicit ConditionEditor(); + + Condition * GetCondition(); + +private: + void SetCriterionEditor(CriterionEditor *pCriterionEditor); + + QVBoxLayout *pMainLayout; + CriterionEditor *pCriterionEditor; +}; + +class CriterionEditor : public QWidget +{ +public: + explicit CriterionEditor(); + + virtual Condition::ConditionCriterion * GetCriterion() = 0; +}; + +enum class SingleCriterionType +{ + FlagSet = 0, + EvidencePresent = 1, + PartnerPresent = 2, + ConversationLocked = 3, + TutorialsEnabled = 4, +}; + +class SingleCriterionEditor : public CriterionEditor +{ + Q_OBJECT + +public: + explicit SingleCriterionEditor(); + + void SetCriterion(SingleCriterionType criterionType, const QString &criterionId = QString()); + void SetIsNegated(bool isNegated); + + Condition::ConditionCriterion * GetCriterion() override; + +public slots: + void CriterionTypeComboBoxCurrentIndexChanged(int newIndex); + void FlagEditorFlagSelected(const QString &flagIdSelected); + void EvidenceSelectorEvidenceSelected(const QString &evidenceIdSelected); + void PartnerSelectorCharacterSelected(const QString &characterIdSelected); + void ConversationLockedEncounterSelectorEncounterSelected(const QString &encounterIdSelected); + void ConversationLockedConversationSelectorConversationSelected(const QString &conversationIdSelected); + +private: + QCheckBox *pNotCheckBox; + QComboBox *pCriterionTypeComboBox; + + FlagEditor *pFlagEditor; + EvidenceSelector *pEvidenceSelector; + CharacterSelector *pPartnerSelector; + + EncounterSelector *pConversationLockedEncounterSelector; + ConversationSelector *pConversationLockedSelector; + + SingleCriterionType criterionTypeSelected; + Flag *pFlagSelected; + Evidence *pEvidenceSelected; + Character *pCharacterSelected; + Encounter *pEncounterSelected; + Conversation *pConversationSelected; +}; + +enum class MultipleCriterionType +{ + And = 0, + Or = 1, +}; + +class MultipleCriterionEditor : public CriterionEditor +{ + Q_OBJECT + +public: + explicit MultipleCriterionEditor(); + + void SetCriterions(MultipleCriterionType criterionType, Condition::ConditionCriterion *pFirstCriterion, Condition::ConditionCriterion *pSecondCriterion); + + Condition::ConditionCriterion * GetCriterion() override; + +public slots: + void CriterionTypeComboBoxCurrentIndexChanged(int newIndex); + +private: + QComboBox *pCriterionTypeComboBox; + + QHBoxLayout *pFirstCriterionHolder; + QHBoxLayout *pSecondCriterionHolder; + + MultipleCriterionType criterionTypeSelected; + CriterionEditor *pFirstCriterionEditor; + CriterionEditor *pSecondCriterionEditor; +}; + +#endif diff --git a/src/CaseCreator/CaseContent/Conversation.cpp b/src/CaseCreator/CaseContent/Conversation.cpp new file mode 100644 index 0000000..7462306 --- /dev/null +++ b/src/CaseCreator/CaseContent/Conversation.cpp @@ -0,0 +1,7873 @@ +#include "Conversation.h" + +#include "CaseContent.h" +#include "Encounter.h" +#include "Character.h" +#include "Evidence.h" +#include "Flag.h" +#include "Location.h" +#include "BackgroundMusic.h" +#include "SoundEffect.h" + +#include "XmlReader.h" +#include "XmlWriter.h" + +#include "SharedUtils.h" +#include "CaseCreator/Utilities/Utilities.h" + +#include "Staging/Conversation.Staging.h" + +#include "CaseCreator/Utilities/AudioPlayer.h" +#include "CaseCreator/Utilities/HtmlDelegate.h" + +#include +#include +#include +#include + +#include +#include +#include + +const QColor AsideTextColor(135, 206, 235); +const QColor EmphasisTextColor(255, 51, 51); + +const double DefaultMillisecondsPerCharacterUpdate = 33; + +template <> +void CaseContent::Add(QString, Conversation *pConversation, void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + pEncounter->AddConversation(pConversation, pConversation->GetId()); + } +} + +template <> +Conversation * CaseContent::GetById(QString id, void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetConversationById(id); + } + else + { + return NULL; + } +} + +template <> +QStringList CaseContent::GetIds(void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetConversationIds(); + } + else + { + return QStringList(); + } +} + +template <> +QStringList CaseContent::GetDisplayNames(void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetConversationDisplayNames(); + } + else + { + return QStringList(); + } +} + +template <> +void CaseContent::Add(QString, Interrogation *pInterrogation, void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + pEncounter->AddConversation(pInterrogation, pInterrogation->GetId()); + } +} + +template <> +Interrogation * CaseContent::GetById(QString id, void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetInterrogationById(id); + } + else + { + return NULL; + } +} + +template <> +QStringList CaseContent::GetIds(void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetInterrogationIds(); + } + else + { + return QStringList(); + } +} + +template <> +QStringList CaseContent::GetDisplayNames(void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetInterrogationDisplayNames(); + } + else + { + return QStringList(); + } +} + +template <> +void CaseContent::Add(QString, Confrontation *pConfrontation, void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + pEncounter->AddConversation(pConfrontation, pConfrontation->GetId()); + } +} + +template <> +Confrontation * CaseContent::GetById(QString id, void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetConfrontationById(id); + } + else + { + return NULL; + } +} + +template <> +QStringList CaseContent::GetIds(void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetConfrontationIds(); + } + else + { + return QStringList(); + } +} + +template <> +QStringList CaseContent::GetDisplayNames(void *pParentObject) +{ + Encounter *pEncounter = reinterpret_cast(pParentObject); + + if (pEncounter != NULL) + { + return pEncounter->GetConfrontationDisplayNames(); + } + else + { + return QStringList(); + } +} + +QString CharacterSelectorMultipleSelectionSelectorWidget::GetString() +{ + return pCharacterSelector->GetSelectedCharacterId(); +} + +void CharacterSelectorMultipleSelectionSelectorWidget::SetToString(const QString &string) +{ + pCharacterSelector->SetToCharacterById(string); +} + +QWidget * CharacterSelectorMultipleSelectionSelectorWidget::CreateSelector() +{ + pCharacterSelector = new CharacterSelector(false /* allowNoCharacter */); + QObject::connect(pCharacterSelector, SIGNAL(CharacterSelected(QString)), this, SLOT(CharacterSelectorCharacterSelected(QString))); + return pCharacterSelector; +} + +void CharacterSelectorMultipleSelectionSelectorWidget::CharacterSelectorCharacterSelected(const QString &characterId) +{ + emit StringSelected(characterId); +} + +QString EvidenceSelectorMultipleSelectionSelectorWidget::GetString() +{ + return pEvidenceSelector->GetId(); +} + +void EvidenceSelectorMultipleSelectionSelectorWidget::SetToString(const QString &string) +{ + pEvidenceSelector->SetToId(string); +} + +QWidget * EvidenceSelectorMultipleSelectionSelectorWidget::CreateSelector() +{ + pEvidenceSelector = new EvidenceSelector(); + QObject::connect(pEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(EvidenceSelectorEvidenceSelected(QString))); + return pEvidenceSelector; +} + +void EvidenceSelectorMultipleSelectionSelectorWidget::EvidenceSelectorEvidenceSelected(const QString &evidenceId) +{ + emit StringSelected(this, evidenceId); +} + +QString LineEditMultipleSelectionSelectorWidget::GetString() +{ + return pLineEdit->text(); +} + +void LineEditMultipleSelectionSelectorWidget::SetToString(const QString &string) +{ + pLineEdit->setText(string); +} + +QWidget * LineEditMultipleSelectionSelectorWidget::CreateSelector() +{ + pLineEdit = new QLineEdit(); + pLineEdit->setMinimumWidth(300); + QObject::connect(pLineEdit, SIGNAL(textEdited(QString)), this, SLOT(LineEditTextEdited(QString))); + return pLineEdit; +} + +void LineEditMultipleSelectionSelectorWidget::LineEditTextEdited(const QString &string) +{ + emit StringSelected(this, string); +} + +QString ConfrontationTopicMultipleSelectionSelectorWidget::GetString() +{ + return GenerateString(); +} + +void ConfrontationTopicMultipleSelectionSelectorWidget::SetToString(const QString &string) +{ + QString id; + QString name; + bool isEnabledAtStart; + + ConfrontationTopicMultipleSelectionSelectorWidget::ParseString(string, &id, &name, &isEnabledAtStart); + + pNameLineEdit->setText(name); + pIsEnabledCheckBox->setChecked(isEnabledAtStart); +} + +QWidget * ConfrontationTopicMultipleSelectionSelectorWidget::CreateSelector() +{ + QWidget *pSelector = new QWidget(); + QHBoxLayout *pLayout = new QHBoxLayout(); + + pNameLineEdit = new QLineEdit(); + pNameLineEdit->setMinimumWidth(300); + QObject::connect(pNameLineEdit, SIGNAL(textEdited(QString)), this, SLOT(NameLineEditTextEdited(QString))); + pLayout->addWidget(pNameLineEdit); + + pIsEnabledCheckBox = new QCheckBox(); + pIsEnabledCheckBox->setText("Enabled at start"); + QObject::connect(pIsEnabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(IsEnabledCheckBoxToggled(bool))); + pLayout->addWidget(pIsEnabledCheckBox); + + pSelector->setLayout(pLayout); + return pSelector; +} + +QString ConfrontationTopicMultipleSelectionSelectorWidget::CreateString(QString id, QString name, bool isEnabledAtStart) +{ + return id + "\t" + name + "\t" + (isEnabledAtStart ? "true" : "false"); +} + +void ConfrontationTopicMultipleSelectionSelectorWidget::ParseString(QString string, QString *pId, QString *pName, bool *pIsEnabledAtStart) +{ + QStringList stringList = string.split("\t"); + + if (stringList.size() != 3) + { + throw new MLIException("Expected three elements in a confrontation topic selector widget string: the ID, the name, and the enabled-at-start state."); + } + + *pId = stringList[0]; + *pName = stringList[1]; + *pIsEnabledAtStart = stringList[2] == "true"; +} + +void ConfrontationTopicMultipleSelectionSelectorWidget::NameLineEditTextEdited(const QString &/*evidenceId*/) +{ + emit StringSelected(this, GenerateString()); +} + +void ConfrontationTopicMultipleSelectionSelectorWidget::IsEnabledCheckBoxToggled(bool /*isChecked*/) +{ + emit StringSelected(this, GenerateString()); +} + +QString ConfrontationTopicMultipleSelectionSelectorWidget::GenerateString() +{ + QString name = pNameLineEdit->text(); + QString id = name.replace(QRegExp("[^a-zA-Z0-9]"), ""); + + return ConfrontationTopicMultipleSelectionSelectorWidget::CreateString(id, name, pIsEnabledCheckBox->isChecked()); +} + +Conversation::Dialog::Dialog() + : ConversationDialogSlots() +{ + pDrawingViewForPlaying = NULL; + currentPosition = -1; + millisecondsPerCharacterUpdate = DefaultMillisecondsPerCharacterUpdate; + + playTimer.setSingleShot(false); + QObject::connect(&playTimer, SIGNAL(timeout()), this, SLOT(HandleTimeout())); + + events.clear(); + colorTransitions.clear(); +} + +Conversation::Dialog::~Dialog() +{ + Reset(); +} + +void Conversation::Dialog::Reset() +{ + for (DialogEvent *pEvent : events) + { + delete pEvent; + } + + events.clear(); + colorTransitions.clear(); +} + +void Conversation::Dialog::AddSpeedChangePosition(int position, double newMillisecondsPerCharacterUpdate) +{ + events.push_back(new SpeedChangeEvent(position, this, newMillisecondsPerCharacterUpdate)); +} + +void Conversation::Dialog::AddEmotionChangePosition(int position, const QString &newEmotionId) +{ + events.push_back(new SpeakerEmotionChangeEvent(position, this, newEmotionId)); +} + +void Conversation::Dialog::AddEmotionOtherChangePosition(int position, const QString &newEmotionId) +{ + events.push_back(new OtherEmotionChangeEvent(position, this, newEmotionId)); +} + +void Conversation::Dialog::AddPausePosition(int position, double millisecondDuration) +{ + int intMillisecondDuration = (int)millisecondDuration; + int pauseDuration = 0; + + if (millisecondDuration > 500) + { + pauseDuration = intMillisecondDuration - 300; + } + else + { + pauseDuration = intMillisecondDuration * 2 / 5; + } + + events.push_back(new PauseEvent(position, this, pauseDuration)); + events.push_back(new AudioPauseEvent(position, this, intMillisecondDuration - pauseDuration)); +} + +void Conversation::Dialog::AddAudioPausePosition(int position, double millisecondDuration) +{ + events.push_back(new AudioPauseEvent(position, this, (int)millisecondDuration)); +} + +void Conversation::Dialog::AddMouthChangePosition(int position, bool mouthIsOn) +{ + events.push_back(new MouthChangeEvent(position, this, mouthIsOn)); +} + +void Conversation::Dialog::StartAside(int position) +{ + colorTransitions.push_back(TextColorTransition(position, AsideTextColor, false /* endsColor */)); +} + +void Conversation::Dialog::EndAside(int position) +{ + colorTransitions.push_back(TextColorTransition(position, AsideTextColor, true /* endsColor */)); +} + +void Conversation::Dialog::StartEmphasis(int position) +{ + colorTransitions.push_back(TextColorTransition(position, EmphasisTextColor, false /* endsColor */)); +} + +void Conversation::Dialog::EndEmphasis(int position) +{ + colorTransitions.push_back(TextColorTransition(position, EmphasisTextColor, true /* endsColor */)); +} + +void Conversation::Dialog::PlaySound(int position, const QString &id) +{ + events.push_back(new PlaySoundEvent(position, this, id)); + soundEffectIds.push_back(id); +} + +QStringList Conversation::Dialog::GetHtmlLines(int lastTextPosition) const +{ + // If lastTextPosition < 0, then we'll retrieve all the text. + if (lastTextPosition < 0) + { + lastTextPosition = parsedDialog.length(); + } + + int lineOffset = 0; + int htmlOffset = 0; + QStack spansOpen; + + QStringList originalLines = split(parsedDialog.left(lastTextPosition), '\n'); + QStringList htmlLines = originalLines; + + QStringList::iterator originalIter = originalLines.begin(); + QStringList::iterator htmlIter = htmlLines.begin(); + + for (TextColorTransition colorTransition : colorTransitions) + { + if (colorTransition.Position > lastTextPosition) + { + break; + } + else + { + while (colorTransition.Position > lineOffset + (*originalIter).length()) + { + lineOffset += (*originalIter).length() + 1; // + 1 for the newline character. + htmlOffset = 0; + + for (int i = 0; i < spansOpen.length(); i++) + { + (*htmlIter).append(""); + } + + originalIter++; + htmlIter++; + + if (htmlIter == htmlLines.end()) + { + break; + } + + for (QString span : spansOpen) + { + (*htmlIter).insert(0, span); + htmlOffset += span.length(); + } + } + } + + if (htmlIter == htmlLines.end()) + { + break; + } + + QString htmlToAdd; + + if (colorTransition.EndsColor) + { + htmlToAdd = ""; + spansOpen.pop(); + } + else + { + int r, g, b; + colorTransition.Color.getRgb(&r, &g, &b); + + char rgbString[256]; + sprintf(rgbString, "%02x%02x%02x", r, g, b); + + htmlToAdd = QString(""); + spansOpen.push(htmlToAdd); + } + + (*htmlIter).insert(colorTransition.Position - lineOffset + htmlOffset, htmlToAdd); + htmlOffset += htmlToAdd.length(); + } + + while (htmlIter != htmlLines.end()) + { + for (int i = 0; i < spansOpen.length(); i++) + { + (*htmlIter).append(""); + } + + htmlIter++; + + if (htmlIter != htmlLines.end()) + { + for (QString span : spansOpen) + { + (*htmlIter).insert(0, span); + htmlOffset += span.length(); + } + } + } + + return htmlLines; +} + +void Conversation::Dialog::PreloadAudio() +{ + AudioPlayer::PreloadDialog(dialogAudioFileName, dialogAudioFileName); + + for (QString id : soundEffectIds) + { + if (CaseContent::GetInstance()->GetIds().contains(id)) + { + CaseContent::GetInstance()->GetById(id)->Preload(); + } + } +} + +void Conversation::Dialog::PlayOnDrawingView(Conversation::DrawingView *pDrawingView) +{ + pDrawingViewForPlaying = pDrawingView; + currentPosition = 0; + leftCharacterEmotionId = leftCharacterInitialEmotionId; + rightCharacterEmotionId = rightCharacterInitialEmotionId; + millisecondsPerCharacterUpdate = DefaultMillisecondsPerCharacterUpdate; + mouthOffCount = 0; + + eventIterator = events.begin(); + + if (dialogAudioFileName.length() > 0) + { + AudioPlayer::PlayDialog(dialogAudioFileName); + } + + playTimer.start(16); + elapsedTimer.start(); + elapsedTimeLeftOver = 0; + msPauseDuration = 0; + msAudioPauseDuration = 0; + IncrementPositionOnTimeout(); +} + +void Conversation::Dialog::StopPlaying() +{ + playTimer.stop(); + AudioPlayer::StopDialog(); + + if (pDrawingViewForPlaying != NULL) + { + pDrawingViewForPlaying->SetDialog(speakerPosition, GetHtmlLines(), speakingCharacterId, false); + pDrawingViewForPlaying = NULL; + } +} + +void Conversation::Dialog::IncrementPositionOnTimeout() +{ + qint64 elapsedTime = elapsedTimer.elapsed() + elapsedTimeLeftOver; + elapsedTimer.start(); + + while (msAudioPauseDuration > 0 || msPauseDuration > 0) + { + if (msAudioPauseDuration > 0) + { + if (msAudioPauseDuration > elapsedTime) + { + msAudioPauseDuration -= elapsedTime; + elapsedTime = 0; + } + else + { + msAudioPauseDuration = 0; + elapsedTime -= msAudioPauseDuration; + } + } + + if (msAudioPauseDuration == 0 && msPauseDuration > 0) + { + if (msPauseDuration > elapsedTime) + { + msPauseDuration -= elapsedTime; + elapsedTime = 0; + } + else + { + msPauseDuration = 0; + elapsedTime -= msPauseDuration; + } + } + + if (msAudioPauseDuration > 0 || msPauseDuration > 0) + { + break; + } + + while (eventIterator != events.end() && (*eventIterator)->GetPosition() <= currentPosition && msAudioPauseDuration == 0 && msPauseDuration == 0) + { + (*eventIterator)->Raise(); + eventIterator++; + } + } + + while (msAudioPauseDuration == 0 && msPauseDuration == 0 && elapsedTime >= millisecondsPerCharacterUpdate && currentPosition < parsedDialog.length()) + { + currentPosition++; + elapsedTime -= millisecondsPerCharacterUpdate; + } + + elapsedTimeLeftOver = elapsedTime; + + while (eventIterator != events.end() && (*eventIterator)->GetPosition() <= currentPosition && msAudioPauseDuration == 0 && msPauseDuration == 0) + { + (*eventIterator)->Raise(); + eventIterator++; + } + + pDrawingViewForPlaying->SetLeftCharacter( + leftCharacterId, + leftCharacterEmotionId); + + pDrawingViewForPlaying->SetRightCharacter( + rightCharacterId, + rightCharacterEmotionId); + + bool isTalking = + msAudioPauseDuration == 0 && + (currentPosition < parsedDialog.length() || + (AudioPlayer::GetCurrentPlayingDialog() == dialogAudioFileName && dialogAudioFileName.length() > 0)) && + mouthOffCount == 0; + + pDrawingViewForPlaying->SetDialog(speakerPosition, GetHtmlLines(currentPosition), speakingCharacterId, isTalking); + + if (currentPosition >= parsedDialog.length() && AudioPlayer::GetCurrentPlayingDialog() != dialogAudioFileName) + { + StopPlaying(); + } +} + +void Conversation::Dialog::SpeedChangeEvent::Raise() +{ + this->pOwningDialog->millisecondsPerCharacterUpdate = newMillisecondsPerCharacterUpdate; +} + +void Conversation::Dialog::SpeakerEmotionChangeEvent::Raise() +{ + if (this->pOwningDialog->speakerPosition == CharacterPositionLeft) + { + this->pOwningDialog->leftCharacterEmotionId = newEmotionId; + } + else if (this->pOwningDialog->speakerPosition == CharacterPositionRight) + { + this->pOwningDialog->rightCharacterEmotionId = newEmotionId; + } +} + +void Conversation::Dialog::OtherEmotionChangeEvent::Raise() +{ + if (this->pOwningDialog->speakerPosition == CharacterPositionLeft) + { + this->pOwningDialog->rightCharacterEmotionId = newEmotionId; + } + else if (this->pOwningDialog->speakerPosition == CharacterPositionRight) + { + this->pOwningDialog->leftCharacterEmotionId = newEmotionId; + } +} + +void Conversation::Dialog::PauseEvent::Raise() +{ + this->pOwningDialog->msPauseDuration += msDuration; +} + +void Conversation::Dialog::AudioPauseEvent::Raise() +{ + this->pOwningDialog->msAudioPauseDuration += msDuration; +} + +void Conversation::Dialog::MouthChangeEvent::Raise() +{ + if (isMouthOpen) + { + if (this->pOwningDialog->mouthOffCount > 0) + { + this->pOwningDialog->mouthOffCount--; + } + } + else + { + this->pOwningDialog->mouthOffCount++; + } +} + +void Conversation::Dialog::PlaySoundEvent::Raise() +{ + if (CaseContent::GetInstance()->GetIds().contains(id)) + { + CaseContent::GetInstance()->GetById(id)->Play(); + } +} + +Conversation::Conversation(Staging::Conversation *pStagingConversation) + : Conversation() +{ + SplitConversationIdFromCaseFile(pStagingConversation->Id, NULL, &id); + name = pStagingConversation->Name; +} + +Conversation::~Conversation() +{ + pOwningEncounter = NULL; +} + +void Conversation::WriteToCaseXml(XmlWriter *pWriter) +{ + pWriter->StartElement("Conversation"); + WriteToCaseXmlCore(pWriter); + pWriter->EndElement(); +} + +void Conversation::WriteToCaseXmlCore(XmlWriter *pWriter) +{ + pWriter->WriteBooleanElement("IsEnabled", false); // Placeholder + pWriter->WriteBooleanElement("HasBeenCompleted", false); // Placeholder + + pWriter->StartElement("ActionList"); + + for (Action *pAction : actions) + { + pWriter->StartElement("Entry"); + pAction->WriteToCaseXml(pWriter); + pWriter->EndElement(); + } + + pWriter->EndElement(); +} + +void Conversation::PopulateActionsFromStaging(QList &actions, const QList &stagingActions, int initialIndex) +{ + for (int i = 0; i < stagingActions.size(); i++) + { + PopulateActionFromStaging(actions, stagingActions, i, initialIndex); + } +} + +void Conversation::PopulateActionFromStaging(QList &actions, const QList &stagingActions, int &indexInStagingActions, int initialIndexInFullList) +{ + Staging::Conversation::Action *pAction = stagingActions[indexInStagingActions]; + + switch (pAction->GetType()) + { + case Staging::ConversationActionType_MultipleCharacterChange: + { + Staging::Conversation::MultipleCharacterChangeAction *pActionSubclass = + static_cast(pAction); + + for (Staging::Conversation::CharacterChangeAction *pCharacterChangeAction : pActionSubclass->CharacterChangeList) + { + actions.append(new CharacterChangeAction(pCharacterChangeAction)); + } + } + break; + + case Staging::ConversationActionType_CharacterChange: + actions.append(new CharacterChangeAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_SetFlag: + { + SetFlagAction *pSetFlagAction = new SetFlagAction(static_cast(pAction)); + + if (indexInStagingActions < stagingActions.count() - 1) + { + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() == Staging::ConversationActionType_Notification) + { + pSetFlagAction->AddNotification(static_cast(pNextAction)); + } + } + + actions.append(pSetFlagAction); + } + break; + + case Staging::ConversationActionType_BranchOnCondition: + { + Staging::Conversation::BranchOnConditionAction *pStagingBranchOnConditionAction = + static_cast(pAction); + + BranchOnConditionAction *pBranchOnConditionAction = new BranchOnConditionAction(pStagingBranchOnConditionAction); + + if (indexInStagingActions >= stagingActions.count() - 1) + { + throw new MLIException("Expected a BranchIfTrueAction to follow a BranchOnConditionAction."); + } + + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() != Staging::ConversationActionType_BranchIfTrue && + pNextAction->GetType() != Staging::ConversationActionType_BranchIfFalse) + { + throw new MLIException("Expected a BranchIfTrueAction or a BranchIfFalseAction to follow a BranchOnConditionAction."); + } + + int endIndex = 0; + + if (pNextAction->GetType() == Staging::ConversationActionType_BranchIfTrue) + { + Staging::Conversation::BranchIfTrueAction *pStagingBranchIfTrueAction = + static_cast(pNextAction); + + endIndex = pStagingBranchIfTrueAction->EndIndex - initialIndexInFullList; + } + else + { + Staging::Conversation::BranchIfFalseAction *pStagingBranchIfFalseAction = + static_cast(pNextAction); + + endIndex = pStagingBranchIfFalseAction->EndIndex - initialIndexInFullList; + } + + QList branchIfTrueActions; + QList branchIfFalseActions; + + if (pStagingBranchOnConditionAction->TrueIndex < pStagingBranchOnConditionAction->FalseIndex) + { + for (int j = pStagingBranchOnConditionAction->TrueIndex - initialIndexInFullList; j < pStagingBranchOnConditionAction->FalseIndex - initialIndexInFullList; j++) + { + branchIfTrueActions.append(stagingActions[j]); + } + + for (int j = pStagingBranchOnConditionAction->FalseIndex - initialIndexInFullList; j < endIndex; j++) + { + branchIfFalseActions.append(stagingActions[j]); + } + } + else + { + for (int j = pStagingBranchOnConditionAction->FalseIndex - initialIndexInFullList; j < pStagingBranchOnConditionAction->TrueIndex - initialIndexInFullList; j++) + { + branchIfFalseActions.append(stagingActions[j]); + } + + for (int j = pStagingBranchOnConditionAction->TrueIndex - initialIndexInFullList; j < endIndex; j++) + { + branchIfTrueActions.append(stagingActions[j]); + } + } + + PopulateActionsFromStaging(pBranchOnConditionAction->trueActions, branchIfTrueActions, pStagingBranchOnConditionAction->TrueIndex - initialIndexInFullList); + PopulateActionsFromStaging(pBranchOnConditionAction->falseActions, branchIfFalseActions, pStagingBranchOnConditionAction->FalseIndex - initialIndexInFullList); + + actions.append(pBranchOnConditionAction); + indexInStagingActions = endIndex - 1; + } + break; + + case Staging::ConversationActionType_ShowDialog: + case Staging::ConversationActionType_ShowDialogAutomatic: + actions.append(new ShowDialogAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_MustPresentEvidence: + { + Staging::Conversation::MustPresentEvidenceAction *pStagingMustPresentEvidenceAction = + static_cast(pAction); + + MustPresentEvidenceAction *pMustPresentEvidenceAction = new MustPresentEvidenceAction(pStagingMustPresentEvidenceAction); + + int endIndex = -1; + + // Track the number of MustPresentEvidenceActions that we've seen, + // so we're sure that we're matching with the right end action. + int mustPresentEvidenceCount = 1; + + for (int j = indexInStagingActions + 1; j < stagingActions.count(); j++) + { + if (stagingActions[j]->GetType() == Staging::ConversationActionType_EndMustPresentEvidence) + { + mustPresentEvidenceCount--; + + if (mustPresentEvidenceCount == 0) + { + endIndex = j; + break; + } + } + else if (stagingActions[j]->GetType() == Staging::ConversationActionType_MustPresentEvidence) + { + mustPresentEvidenceCount++; + } + } + + if (endIndex < 0) + { + throw new MLIException("Expected an EndMustPresentEvidenceAction to follow a MustPresentEvidenceAction."); + } + + QList correctEvidencePresentedStagingActions; + QList wrongEvidencePresentedStagingActions; + QList endRequestedStagingActions; + + int firstIndex = pStagingMustPresentEvidenceAction->CorrectEvidencePresentedIndex; + int secondIndex = -1; + int thirdIndex = -1; + QList *pFirstStagingActions = &correctEvidencePresentedStagingActions; + QList *pSecondStagingActions = NULL; + QList *pThirdStagingActions = NULL; + + if (pStagingMustPresentEvidenceAction->WrongEvidencePresentedIndex >= 0) + { + if (pStagingMustPresentEvidenceAction->WrongEvidencePresentedIndex < firstIndex) + { + secondIndex = firstIndex; + pSecondStagingActions = pFirstStagingActions; + + firstIndex = pStagingMustPresentEvidenceAction->WrongEvidencePresentedIndex; + pFirstStagingActions = &wrongEvidencePresentedStagingActions; + } + else + { + secondIndex = pStagingMustPresentEvidenceAction->WrongEvidencePresentedIndex; + pSecondStagingActions = &wrongEvidencePresentedStagingActions; + } + } + + if (pStagingMustPresentEvidenceAction->EndRequestedIndex >= 0) + { + if (pStagingMustPresentEvidenceAction->EndRequestedIndex < firstIndex) + { + thirdIndex = secondIndex; + pThirdStagingActions = pSecondStagingActions; + + secondIndex = firstIndex; + pSecondStagingActions = pFirstStagingActions; + + firstIndex = pStagingMustPresentEvidenceAction->EndRequestedIndex; + pFirstStagingActions = &endRequestedStagingActions; + } + else if (pStagingMustPresentEvidenceAction->EndRequestedIndex < secondIndex) + { + thirdIndex = secondIndex; + pThirdStagingActions = pSecondStagingActions; + + secondIndex = pStagingMustPresentEvidenceAction->EndRequestedIndex; + pSecondStagingActions = &endRequestedStagingActions; + } + else + { + thirdIndex = pStagingMustPresentEvidenceAction->EndRequestedIndex; + pThirdStagingActions = &endRequestedStagingActions; + } + } + + if (firstIndex >= 0) + { + int nextIndex = (secondIndex >= 0 ? secondIndex - initialIndexInFullList : (thirdIndex >= 0 ? thirdIndex - initialIndexInFullList : endIndex)); + + for (int j = firstIndex - initialIndexInFullList; j < nextIndex; j++) + { + pFirstStagingActions->append(stagingActions[j]); + } + } + + if (secondIndex >= 0) + { + int nextIndex = (thirdIndex >= 0 ? thirdIndex - initialIndexInFullList : endIndex); + + for (int j = secondIndex - initialIndexInFullList; j < nextIndex; j++) + { + pSecondStagingActions->append(stagingActions[j]); + } + } + + if (thirdIndex >= 0) + { + for (int j = thirdIndex - initialIndexInFullList; j < endIndex; j++) + { + pThirdStagingActions->append(stagingActions[j]); + } + } + + if (pStagingMustPresentEvidenceAction->CorrectEvidencePresentedIndex >= 0) + { + PopulateActionsFromStaging(pMustPresentEvidenceAction->correctEvidencePresentedActions, correctEvidencePresentedStagingActions, pStagingMustPresentEvidenceAction->CorrectEvidencePresentedIndex - initialIndexInFullList); + } + + if (pStagingMustPresentEvidenceAction->WrongEvidencePresentedIndex >= 0) + { + PopulateActionsFromStaging(pMustPresentEvidenceAction->wrongEvidencePresentedActions, wrongEvidencePresentedStagingActions, pStagingMustPresentEvidenceAction->WrongEvidencePresentedIndex - initialIndexInFullList); + } + + if (pStagingMustPresentEvidenceAction->EndRequestedIndex >= 0) + { + PopulateActionsFromStaging(pMustPresentEvidenceAction->endRequestedActions, endRequestedStagingActions, pStagingMustPresentEvidenceAction->EndRequestedIndex - initialIndexInFullList); + } + + actions.append(pMustPresentEvidenceAction); + indexInStagingActions = endIndex - 1; + } + break; + + case Staging::ConversationActionType_EnableConversation: + { + EnableConversationAction *pEnableConversationAction = new EnableConversationAction(static_cast(pAction)); + + if (indexInStagingActions < stagingActions.count() - 1) + { + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() == Staging::ConversationActionType_Notification) + { + pEnableConversationAction->AddNotification(static_cast(pNextAction)); + } + } + + actions.append(pEnableConversationAction); + } + break; + + case Staging::ConversationActionType_EnableEvidence: + { + EnableEvidenceAction *pEnableEvidenceAction = new EnableEvidenceAction(static_cast(pAction)); + + if (indexInStagingActions < stagingActions.count() - 1) + { + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() == Staging::ConversationActionType_Notification) + { + pEnableEvidenceAction->AddNotification(static_cast(pNextAction)); + } + } + + actions.append(pEnableEvidenceAction); + } + break; + + case Staging::ConversationActionType_UpdateEvidence: + { + UpdateEvidenceAction *pUpdateEvidenceAction = new UpdateEvidenceAction(static_cast(pAction)); + + if (indexInStagingActions < stagingActions.count() - 1) + { + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() == Staging::ConversationActionType_Notification) + { + pUpdateEvidenceAction->AddNotification(static_cast(pNextAction)); + } + } + + actions.append(pUpdateEvidenceAction); + } + break; + + case Staging::ConversationActionType_DisableEvidence: + { + DisableEvidenceAction *pDisableEvidenceAction = new DisableEvidenceAction(static_cast(pAction)); + + if (indexInStagingActions < stagingActions.count() - 1) + { + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() == Staging::ConversationActionType_Notification) + { + pDisableEvidenceAction->AddNotification(static_cast(pNextAction)); + } + } + + actions.append(pDisableEvidenceAction); + } + break; + + case Staging::ConversationActionType_EnableCutscene: + { + EnableCutsceneAction *pEnableCutsceneAction = new EnableCutsceneAction(static_cast(pAction)); + + if (indexInStagingActions < stagingActions.count() - 1) + { + Staging::Conversation::Action *pNextAction = stagingActions[indexInStagingActions + 1]; + + if (pNextAction->GetType() == Staging::ConversationActionType_Notification) + { + pEnableCutsceneAction->AddNotification(static_cast(pNextAction)); + } + } + + actions.append(pEnableCutsceneAction); + } + break; + + case Staging::ConversationActionType_PlayBgm: + actions.append(new PlayBgmAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_PauseBgm: + actions.append(new PauseBgmAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_ResumeBgm: + actions.append(new ResumeBgmAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_StopBgm: + actions.append(new StopBgmAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_PlayAmbiance: + actions.append(new PlayAmbianceAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_PauseAmbiance: + actions.append(new PauseAmbianceAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_ResumeAmbiance: + actions.append(new ResumeAmbianceAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_StopAmbiance: + actions.append(new StopAmbianceAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_StartAnimation: + actions.append(new StartAnimationAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_StopAnimation: + actions.append(new StopAnimationAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_SetPartner: + actions.append(new SetPartnerAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_GoToPresentWrongEvidence: + actions.append(new GoToPresentWrongEvidenceAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_LockConversation: + actions.append(new LockConversationAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_ExitEncounter: + actions.append(new ExitEncounterAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_MoveToLocation: + actions.append(new MoveToLocationAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_MoveToZoomedView: + actions.append(new MoveToZoomedViewAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_EndCase: + actions.append(new EndCaseAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_BeginMultipleChoice: + { + Staging::Conversation::BeginMultipleChoiceAction *pStagingBeginMultipleChoiceAction = + static_cast(pAction); + + if (pStagingBeginMultipleChoiceAction->OptionTexts.count() != pStagingBeginMultipleChoiceAction->OptionIndexes.count()) + { + throw new MLIException("Expected there to be an equal number of option texts and option indexes in MultipleChoiceActions."); + } + + MultipleChoiceAction *pMultipleChoiceAction = new MultipleChoiceAction(pStagingBeginMultipleChoiceAction); + + int endIndex = -1; + + // Track the number of BeginMultipleChoiceActions that we've seen, + // so we're sure that we're matching with the right end action. + int beginMultipleChoiceCount = 1; + + for (int j = indexInStagingActions + 1; j < stagingActions.count(); j++) + { + if (stagingActions[j]->GetType() == Staging::ConversationActionType_EndMultipleChoice) + { + beginMultipleChoiceCount--; + + if (beginMultipleChoiceCount == 0) + { + endIndex = j; + break; + } + } + else if (stagingActions[j]->GetType() == Staging::ConversationActionType_BeginMultipleChoice) + { + beginMultipleChoiceCount++; + } + } + + if (endIndex < 0) + { + throw new MLIException("Expected a EndMultipleChoiceAction to follow a BeginMultipleChoiceAction."); + } + + for (int j = 0; j < pStagingBeginMultipleChoiceAction->OptionIndexes.count(); j++) + { + QList optionActions; + QList optionStagingActions; + + for (int k = pStagingBeginMultipleChoiceAction->OptionIndexes[j] - initialIndexInFullList; + k < (j < pStagingBeginMultipleChoiceAction->OptionIndexes.count() - 1 ? + pStagingBeginMultipleChoiceAction->OptionIndexes[j + 1] - initialIndexInFullList : + endIndex); + k++) + { + optionStagingActions.append(stagingActions[k]); + } + + PopulateActionsFromStaging(optionActions, optionStagingActions, pStagingBeginMultipleChoiceAction->OptionIndexes[j] - initialIndexInFullList); + pMultipleChoiceAction->AddOption(pStagingBeginMultipleChoiceAction->OptionTexts[j], optionActions); + } + + actions.append(pMultipleChoiceAction); + indexInStagingActions = endIndex - 1; + } + break; + + case Staging::ConversationActionType_ExitMultipleChoice: + actions.append(new ExitMultipleChoiceAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_EnableFastForward: + actions.append(new EnableFastForwardAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_DisableFastForward: + actions.append(new DisableFastForwardAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_BeginBreakdown: + actions.append(new BeginBreakdownAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_EndBreakdown: + actions.append(new EndBreakdownAction(static_cast(pAction))); + break; + + default: + qDebug("Ignoring conversation action of type '%s'.", ConversationActionTypeToString(pAction->GetType()).toStdString().c_str()); + break; + } +} + +void Conversation::UpdateAndCacheConversationStates() +{ + State state; + + if (pOwningEncounter != NULL) + { + state.SetLeftCharacterId(pOwningEncounter->GetInitialLeftCharacterId()); + state.SetLeftCharacterEmotionId(pOwningEncounter->GetInitialLeftCharacterEmotionId()); + state.SetRightCharacterId(pOwningEncounter->GetInitialRightCharacterId()); + state.SetRightCharacterEmotionId(pOwningEncounter->GetInitialRightCharacterEmotionId()); + } + + for (Action *pAction : actions) + { + pAction->UpdateAndCacheConversationState(state); + } +} + +void Conversation::ReplaceAction(Action *pNewAction, Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, actions); +} + +void Conversation::ReplaceAction(Action *pNewAction, Action *pOldAction, QList &actionListToSearch) +{ + for (int i = 0; i < actionListToSearch.length(); i++) + { + if (pOldAction == actionListToSearch[i]) + { + actionListToSearch[i] = pNewAction; + delete pOldAction; + break; + } + } +} + +void Conversation::PreloadAudio() +{ + for (Action *pAction : actions) + { + pAction->PreloadAudio(); + } +} + +ObjectListWidget * Conversation::GetObjectListWidget() +{ + ObjectListWidget *pListWidget = new ObjectListWidget(); + + pListWidget->BeginListItemChanges(); + pListWidget->AddListItems(Conversation::Action::GetListItemsForActions(actions, 0)); + pListWidget->NotifyListItemChangesComplete(); + pListWidget->UpdateListItemStatesBeforeObject(); + + return pListWidget; +} + +Conversation::DrawingView::DrawingView(Conversation *pConversation, IDrawable *pParent) + : IDrawable(pParent) + , IDrawingView(pConversation, pParent) +{ + pDialogBackgroundPixmapItem = new QGraphicsPixmapItem(QPixmap(CaseContent::GetInstance()->RelativePathToAbsolutePath("Images/Misc/DialogBackground.png"))); + + pLeftCharacterDialogDrawingView = NULL; + pRightCharacterDialogDrawingView = NULL; + + pDialogItemGroup = new QGraphicsItemGroup(); + pDialogItemGroupOpacityEffect = new QGraphicsOpacityEffect(); + pDialogItemGroupOpacityEffect->setOpacity(0); + pDialogItemGroup->setGraphicsEffect(pDialogItemGroupOpacityEffect); + + pCharacterNameTabPixmapItem = new QGraphicsPixmapItem(QPixmap(CaseContent::GetInstance()->RelativePathToAbsolutePath("Images/Misc/Tab.png"))); + pCharacterNameTabPixmapItem->setY(363 - pCharacterNameTabPixmapItem->pixmap().height()); + pCharacterNameSimpleTextItem = new QGraphicsSimpleTextItem(""); + pCharacterNameSimpleTextItem->setFont(QFont("Celestia Redux", 15)); + pCharacterNameSimpleTextItem->setBrush(QBrush(QColor(255, 255, 255))); + + pDialogItemGroup->addToGroup(pCharacterNameTabPixmapItem); + pDialogItemGroup->addToGroup(pCharacterNameSimpleTextItem); + + QFont dialogFont("FayesMousewriting", 28); + QFontMetrics dialogFontMetrics(dialogFont); + + for (int i = 0; i < 5; i++) + { + pDialogLinesTextItems[i] = new QGraphicsTextItem(""); + pDialogLinesTextItems[i]->setFont(dialogFont); + pDialogLinesTextItems[i]->setDefaultTextColor(QColor(255, 255, 255)); + pDialogLinesTextItems[i]->setX(dialogTextArea.GetX() + dialogPadding); + pDialogLinesTextItems[i]->setY(dialogTextArea.GetY() + dialogPadding + i * dialogFontMetrics.height()); + pDialogItemGroup->addToGroup(pDialogLinesTextItems[i]); + } +} + +Conversation::DrawingView::~DrawingView() +{ + Reset(); + + /*delete pDialogBackgroundPixmapItem; + pDialogBackgroundPixmapItem = NULL; + + delete pLeftCharacterDialogDrawingView; + pLeftCharacterDialogDrawingView = NULL; + + delete pRightCharacterDialogDrawingView; + pRightCharacterDialogDrawingView = NULL; + + delete pDialogItemGroup; + pDialogItemGroup = NULL;*/ +} + +void Conversation::DrawingView::DrawCore(QGraphicsScene *pScene, QList &addedItems) +{ + pScene->addItem(pDialogBackgroundPixmapItem); + addedItems.append(pDialogBackgroundPixmapItem); + + pScene->addItem(pDialogItemGroup); + addedItems.append(pDialogItemGroup); +} + +void Conversation::DrawingView::DrawChildren(QGraphicsScene *pScene) +{ + if (pLeftCharacterDialogDrawingView != NULL) + { + pLeftCharacterDialogDrawingView->Draw(pScene); + } + + if (pRightCharacterDialogDrawingView != NULL) + { + pRightCharacterDialogDrawingView->Draw(pScene); + } +} + +void Conversation::DrawingView::UpdateCore() +{ + pDialogBackgroundPixmapItem->setZValue(1); + pDialogItemGroup->setZValue(2); +} + +void Conversation::DrawingView::UpdateChildren() +{ + if (pLeftCharacterDialogDrawingView != NULL) + { + pLeftCharacterDialogDrawingView->Update(); + } + + if (pRightCharacterDialogDrawingView != NULL) + { + pRightCharacterDialogDrawingView->Update(); + } +} + +void Conversation::DrawingView::ResetChildren() +{ + if (pLeftCharacterDialogDrawingView != NULL) + { + pLeftCharacterDialogDrawingView->Reset(); + } + + if (pRightCharacterDialogDrawingView != NULL) + { + pRightCharacterDialogDrawingView->Reset(); + } +} + +void Conversation::DrawingView::SetLeftCharacter(QString characterId, QString emotionId) +{ + if (characterId != currentLeftCharacterId || emotionId != currentLeftCharacterEmotionId) + { + if (characterId != currentLeftCharacterId) + { + delete pLeftCharacterDialogDrawingView; + + if (characterId.length() > 0) + { + Character *pNewCharacter = CaseContent::GetInstance()->GetById(characterId); + pLeftCharacterDialogDrawingView = pNewCharacter->GetDialogDrawingView(this); + pLeftCharacterDialogDrawingView->SetEmotion(emotionId); + pLeftCharacterDialogDrawingView->SetPosition(Vector2(0, 0)); + pLeftCharacterDialogDrawingView->SetIsFlipped(true); + } + else + { + pLeftCharacterDialogDrawingView = NULL; + } + } + else if (pLeftCharacterDialogDrawingView != NULL) + { + pLeftCharacterDialogDrawingView->SetEmotion(emotionId); + } + + currentLeftCharacterId = characterId; + currentLeftCharacterEmotionId = emotionId; + + Redraw(); + } +} + +void Conversation::DrawingView::SetRightCharacter(QString characterId, QString emotionId) +{ + if (characterId != currentRightCharacterId || emotionId != currentRightCharacterEmotionId) + { + if (characterId != currentRightCharacterId) + { + delete pRightCharacterDialogDrawingView; + + if (characterId.length() > 0) + { + Character *pNewCharacter = CaseContent::GetInstance()->GetById(characterId); + pRightCharacterDialogDrawingView = pNewCharacter->GetDialogDrawingView(this); + pRightCharacterDialogDrawingView->SetEmotion(emotionId); + pRightCharacterDialogDrawingView->SetPosition(Vector2(480, 0)); + } + else + { + pRightCharacterDialogDrawingView = NULL; + } + } + else if (pRightCharacterDialogDrawingView != NULL) + { + pRightCharacterDialogDrawingView->SetEmotion(emotionId); + } + + currentRightCharacterId = characterId; + currentRightCharacterEmotionId = emotionId; + + Redraw(); + } +} + +void Conversation::DrawingView::SetDialog(CharacterPosition characterPosition, const QStringList &dialogHtmlLines, QString offscreenCharacterId, bool isTalking) +{ + if (pLeftCharacterDialogDrawingView != NULL) + { + if (characterPosition == CharacterPositionLeft) + { + pLeftCharacterDialogDrawingView->SetIsSpeaking(isTalking); + } + else + { + pLeftCharacterDialogDrawingView->SetIsSpeaking(false); + } + } + + if (pRightCharacterDialogDrawingView != NULL) + { + if (characterPosition == CharacterPositionRight) + { + pRightCharacterDialogDrawingView->SetIsSpeaking(isTalking); + } + else + { + pRightCharacterDialogDrawingView->SetIsSpeaking(false); + } + } + + if (characterPosition == CharacterPositionNone) + { + pDialogItemGroupOpacityEffect->setOpacity(0); + } + else + { + QString characterId; + + if (characterPosition == CharacterPositionLeft) + { + characterId = currentLeftCharacterId; + } + else if (characterPosition == CharacterPositionRight) + { + characterId = currentRightCharacterId; + } + else if (characterPosition == CharacterPositionOffscreen) + { + characterId = offscreenCharacterId; + } + + if (characterId.length() > 0) + { + pCharacterNameSimpleTextItem->setText(CaseContent::GetInstance()->GetById(characterId)->GetDisplayName().toUpper()); + } + else + { + pCharacterNameSimpleTextItem->setText("????"); + } + + QFontMetrics characterNameFontMetrics(pCharacterNameSimpleTextItem->font()); + pCharacterNameSimpleTextItem->setX(pCharacterNameTabPixmapItem->pixmap().width() / 2 - characterNameFontMetrics.width(pCharacterNameSimpleTextItem->text()) / 2); + pCharacterNameSimpleTextItem->setY(pCharacterNameTabPixmapItem->y() + 1); + + for (int i = 0; i < Min(dialogHtmlLines.length(), 5); i++) + { + pDialogLinesTextItems[i]->setHtml(dialogHtmlLines[i]); + } + + for (int i = Min(dialogHtmlLines.length(), 5); i < 5; i++) + { + pDialogLinesTextItems[i]->setHtml(""); + } + + pDialogItemGroupOpacityEffect->setOpacity(1); + } +} + +Conversation::UnlockCondition * Conversation::UnlockCondition::LoadFromStagingObject(Staging::Conversation::UnlockCondition *pStagingUnlockCondition) +{ + switch (pStagingUnlockCondition->GetType()) + { + case Staging::UnlockConditionType_FlagSet: + return new Conversation::FlagSetUnlockCondition(static_cast(pStagingUnlockCondition)); + + case Staging::UnlockConditionType_PartnerPresent: + return new Conversation::PartnerPresentUnlockCondition(static_cast(pStagingUnlockCondition)); + + default: + throw new MLIException("Unexpected unlock condition type."); + } +} + +Conversation::UnlockCondition * Conversation::UnlockCondition::CreateFromXml(XmlReader *pReader) +{ + if (pReader->ElementExists(Conversation::FlagSetUnlockCondition::GetNameForXml())) + { + return new Conversation::FlagSetUnlockCondition(pReader); + } + else if (pReader->ElementExists(Conversation::PartnerPresentUnlockCondition::GetNameForXml())) + { + return new Conversation::PartnerPresentUnlockCondition(pReader); + } + else + { + throw new MLIException("Unexpected unlock condition type."); + } +} + +Conversation::FlagSetUnlockCondition::FlagSetUnlockCondition(Staging::Conversation::FlagSetUnlockCondition *pStagingFlagSetUnlockCondition) +{ + flagId = pStagingFlagSetUnlockCondition->FlagId; +} + +Conversation::UnlockCondition * Conversation::FlagSetUnlockCondition::Clone() +{ + Conversation::FlagSetUnlockCondition *pClonedCondition = new Conversation::FlagSetUnlockCondition(); + + pClonedCondition->flagId = flagId; + + return pClonedCondition; +} + +QString Conversation::FlagSetUnlockCondition::GetDisplayString() +{ + return QString("Lock this conversation until the flag ") + UnderlineString(flagId) + QString(" is set."); +} + +Conversation::PartnerPresentUnlockCondition::PartnerPresentUnlockCondition(Staging::Conversation::PartnerPresentUnlockCondition *pStagingPartnerPresentUnlockCondition) +{ + partnerId = pStagingPartnerPresentUnlockCondition->PartnerId; +} + +Conversation::UnlockCondition * Conversation::PartnerPresentUnlockCondition::Clone() +{ + Conversation::PartnerPresentUnlockCondition *pClonedCondition = new Conversation::PartnerPresentUnlockCondition(); + + pClonedCondition->partnerId = partnerId; + + return pClonedCondition; +} + +QString Conversation::PartnerPresentUnlockCondition::GetDisplayString() +{ + return QString("Lock this conversation until the partner ") + UnderlineString(partnerId) + QString(" is present."); +} + +bool Conversation::State::SetLeftCharacterId(const QString &id) +{ + Character *pCharacter = CaseContent::GetInstance()->GetById(id); + + if (pCharacter != NULL) + { + this->leftCharacterId = id; + return true; + } + else + { + return false; + } +} + +bool Conversation::State::SetLeftCharacterEmotionId(const QString &id) +{ + Character *pCharacter = CaseContent::GetInstance()->GetById(leftCharacterId); + + if (pCharacter != NULL) + { + QString newEmotionId = ""; + + for (const QString &emotionId : pCharacter->GetEmotionIds()) + { + if (QRegExp(emotionId, Qt::CaseInsensitive).exactMatch(id)) + { + newEmotionId = id; + break; + } + } + + if (newEmotionId.length() > 0) + { + this->leftCharacterEmotionId = newEmotionId; + return true; + } + } + + return false; +} + +bool Conversation::State::SetRightCharacterId(const QString &id) +{ + Character *pCharacter = CaseContent::GetInstance()->GetById(id); + + if (pCharacter != NULL) + { + this->rightCharacterId = id; + return true; + } + else + { + return false; + } +} + +bool Conversation::State::SetRightCharacterEmotionId(const QString &id) +{ + Character *pCharacter = CaseContent::GetInstance()->GetById(rightCharacterId); + + if (pCharacter != NULL) + { + QString newEmotionId = ""; + + for (const QString &emotionId : pCharacter->GetEmotionIds()) + { + if (QRegExp(emotionId, Qt::CaseInsensitive).exactMatch(id)) + { + newEmotionId = id; + break; + } + } + + if (newEmotionId.length() > 0) + { + this->rightCharacterEmotionId = newEmotionId; + return true; + } + } + + return false; +} + +Conversation::Action * Conversation::Action::Clone() +{ + Conversation::Action *pClonedAction = NULL; + + switch(GetType()) + { + case Conversation::ActionType::CharacterChange: + pClonedAction = new Conversation::CharacterChangeAction(); + break; + case Conversation::ActionType::SetFlag: + pClonedAction = new Conversation::SetFlagAction(); + break; + case Conversation::ActionType::BranchOnCondition: + pClonedAction = new Conversation::BranchOnConditionAction(); + break; + case Conversation::ActionType::ShowDialog: + pClonedAction = new Conversation::ShowDialogAction(); + break; + case Conversation::ActionType::MustPresentEvidence: + pClonedAction = new Conversation::MustPresentEvidenceAction(); + break; + case Conversation::ActionType::EnableConversation: + pClonedAction = new Conversation::EnableConversationAction(); + break; + case Conversation::ActionType::EnableEvidence: + pClonedAction = new Conversation::EnableEvidenceAction(); + break; + case Conversation::ActionType::UpdateEvidence: + pClonedAction = new Conversation::UpdateEvidenceAction(); + break; + case Conversation::ActionType::DisableEvidence: + pClonedAction = new Conversation::DisableEvidenceAction(); + break; + case Conversation::ActionType::EnableCutscene: + pClonedAction = new Conversation::EnableCutsceneAction(); + break; + case Conversation::ActionType::PlayBgm: + pClonedAction = new Conversation::PlayBgmAction(); + break; + case Conversation::ActionType::PauseBgm: + pClonedAction = new Conversation::PauseBgmAction(); + break; + case Conversation::ActionType::ResumeBgm: + pClonedAction = new Conversation::ResumeBgmAction(); + break; + case Conversation::ActionType::StopBgm: + pClonedAction = new Conversation::StopBgmAction(); + break; + case Conversation::ActionType::PlayAmbiance: + pClonedAction = new Conversation::PlayAmbianceAction(); + break; + case Conversation::ActionType::PauseAmbiance: + pClonedAction = new Conversation::PauseAmbianceAction(); + break; + case Conversation::ActionType::ResumeAmbiance: + pClonedAction = new Conversation::ResumeAmbianceAction(); + break; + case Conversation::ActionType::StopAmbiance: + pClonedAction = new Conversation::StopAmbianceAction(); + break; + case Conversation::ActionType::StartAnimation: + pClonedAction = new Conversation::StartAnimationAction(); + break; + case Conversation::ActionType::StopAnimation: + pClonedAction = new Conversation::StopAnimationAction(); + break; + case Conversation::ActionType::SetPartner: + pClonedAction = new Conversation::SetPartnerAction(); + break; + case Conversation::ActionType::GoToPresentWrongEvidence: + pClonedAction = new Conversation::GoToPresentWrongEvidenceAction(); + break; + case Conversation::ActionType::LockConversation: + pClonedAction = new Conversation::LockConversationAction(); + break; + case Conversation::ActionType::ExitEncounter: + pClonedAction = new Conversation::ExitEncounterAction(); + break; + case Conversation::ActionType::MoveToLocation: + pClonedAction = new Conversation::MoveToLocationAction(); + break; + case Conversation::ActionType::MoveToZoomedView: + pClonedAction = new Conversation::MoveToZoomedViewAction(); + break; + case Conversation::ActionType::EndCase: + pClonedAction = new Conversation::EndCaseAction(); + break; + case Conversation::ActionType::MultipleChoice: + pClonedAction = new Conversation::MultipleChoiceAction(); + break; + case Conversation::ActionType::ExitMultipleChoice: + pClonedAction = new Conversation::ExitMultipleChoiceAction(); + break; + case Conversation::ActionType::EnableFastForward: + pClonedAction = new Conversation::EnableFastForwardAction(); + break; + case Conversation::ActionType::DisableFastForward: + pClonedAction = new Conversation::DisableFastForwardAction(); + break; + case Conversation::ActionType::BeginBreakdown: + pClonedAction = new Conversation::BeginBreakdownAction(); + break; + case Conversation::ActionType::EndBreakdown: + pClonedAction = new Conversation::EndBreakdownAction(); + break; + case Conversation::ActionType::InterrogationRepeat: + pClonedAction = new Interrogation::InterrogationRepeatAction(); + break; + case Conversation::ActionType::ShowInterrogation: + pClonedAction = new Interrogation::ShowInterrogationAction(); + break; + case Conversation::ActionType::ExitInterrogationRepeat: + pClonedAction = new Interrogation::ExitInterrogationRepeatAction(); + break; + case Conversation::ActionType::ConfrontationTopicSelection: + pClonedAction = new Confrontation::ConfrontationTopicSelectionAction(); + break; + case Conversation::ActionType::EnableTopic: + pClonedAction = new Confrontation::EnableTopicAction(); + break; + case Conversation::ActionType::RestartDecision: + pClonedAction = new Confrontation::RestartDecisionAction(); + break; + case Conversation::ActionType::RestartConfrontation: + pClonedAction = new Confrontation::RestartConfrontationAction(); + break; + default: + throw new MLIException("Unknown action type"); + } + + CopyProperties(pClonedAction, true /* isForEdit */); + return pClonedAction; +} + +void Conversation::Action::CopyProperties(ListItemObject *pOther, bool isForEdit) +{ + if (!IsSameType(pOther)) + { + throw new MLIException("Can only copy properties between actions of the same type."); + } + + CopyPropertiesImpl(dynamic_cast(pOther), isForEdit); +} + +void Conversation::Action::ExchangeListItemBaseOwnership(ListItemObject *pOther) +{ + if (GetType() != pOther->GetType()) + { + throw new MLIException("Can only exchange ListItemBase ownership between actions of the same type."); + } + + ListItemBase *pListItemBase = GetListItemBase(); + Conversation::Action *pAction = dynamic_cast(pOther); + + if (pListItemBase != NULL) + { + pListItemBase->SetObject(pAction); + } + + ExchangeListItemBaseOwnershipImpl(dynamic_cast(pOther)); +} + +void Conversation::Action::CloneActionList(QList &destination, const QList &source) +{ + for (Action *pAction : destination) + { + delete pAction; + } + + destination.clear(); + + for (Action *pAction : source) + { + destination.push_back(pAction->Clone()); + } +} + +void Conversation::Action::ExchangeListItemBaseOwnership(const QList &destination, const QList &source) +{ + if (destination.size() != source.size()) + { + throw new MLIException("ExchangeListItemBaseOwnership must only be called on cloned lists of actions."); + } + + for (int i = 0; i < destination.size(); i++) + { + Action *pSourceAction = source[i]; + Action *pDestinationAction = destination[i]; + + pSourceAction->ExchangeListItemBaseOwnership(pDestinationAction); + } +} + +Conversation::Action * Conversation::Action::CreateFromXml(XmlReader *pReader) +{ + if (pReader->ElementExists(CharacterChangeAction::GetNameForXml())) + { + return new CharacterChangeAction(pReader); + } + else if (pReader->ElementExists(SetFlagAction::GetNameForXml())) + { + return new SetFlagAction(pReader); + } + else if (pReader->ElementExists(BranchOnConditionAction::GetNameForXml())) + { + return new BranchOnConditionAction(pReader); + } + else if (pReader->ElementExists(ShowDialogAction::GetNameForXml())) + { + return new ShowDialogAction(pReader); + } + else if (pReader->ElementExists(MustPresentEvidenceAction::GetNameForXml())) + { + return new MustPresentEvidenceAction(pReader); + } + else if (pReader->ElementExists(EnableConversationAction::GetNameForXml())) + { + return new EnableConversationAction(pReader); + } + else if (pReader->ElementExists(EnableEvidenceAction::GetNameForXml())) + { + return new EnableEvidenceAction(pReader); + } + else if (pReader->ElementExists(UpdateEvidenceAction::GetNameForXml())) + { + return new UpdateEvidenceAction(pReader); + } + else if (pReader->ElementExists(DisableEvidenceAction::GetNameForXml())) + { + return new DisableEvidenceAction(pReader); + } + else if (pReader->ElementExists(EnableCutsceneAction::GetNameForXml())) + { + return new EnableCutsceneAction(pReader); + } + else if (pReader->ElementExists(PlayBgmAction::GetNameForXml())) + { + return new PlayBgmAction(pReader); + } + else if (pReader->ElementExists(PauseBgmAction::GetNameForXml())) + { + return new PauseBgmAction(pReader); + } + else if (pReader->ElementExists(ResumeBgmAction::GetNameForXml())) + { + return new ResumeBgmAction(pReader); + } + else if (pReader->ElementExists(StopBgmAction::GetNameForXml())) + { + return new StopBgmAction(pReader); + } + else if (pReader->ElementExists(PlayAmbianceAction::GetNameForXml())) + { + return new PlayAmbianceAction(pReader); + } + else if (pReader->ElementExists(PauseAmbianceAction::GetNameForXml())) + { + return new PauseAmbianceAction(pReader); + } + else if (pReader->ElementExists(ResumeAmbianceAction::GetNameForXml())) + { + return new ResumeAmbianceAction(pReader); + } + else if (pReader->ElementExists(StopAmbianceAction::GetNameForXml())) + { + return new StopAmbianceAction(pReader); + } + else if (pReader->ElementExists(StartAnimationAction::GetNameForXml())) + { + return new StartAnimationAction(pReader); + } + else if (pReader->ElementExists(StopAnimationAction::GetNameForXml())) + { + return new StopAnimationAction(pReader); + } + else if (pReader->ElementExists(SetPartnerAction::GetNameForXml())) + { + return new SetPartnerAction(pReader); + } + else if (pReader->ElementExists(GoToPresentWrongEvidenceAction::GetNameForXml())) + { + return new GoToPresentWrongEvidenceAction(pReader); + } + else if (pReader->ElementExists(LockConversationAction::GetNameForXml())) + { + return new LockConversationAction(pReader); + } + else if (pReader->ElementExists(ExitEncounterAction::GetNameForXml())) + { + return new ExitEncounterAction(pReader); + } + else if (pReader->ElementExists(MoveToLocationAction::GetNameForXml())) + { + return new MoveToLocationAction(pReader); + } + else if (pReader->ElementExists(MoveToZoomedViewAction::GetNameForXml())) + { + return new MoveToZoomedViewAction(pReader); + } + else if (pReader->ElementExists(EndCaseAction::GetNameForXml())) + { + return new EndCaseAction(pReader); + } + else if (pReader->ElementExists(MultipleChoiceAction::GetNameForXml())) + { + return new MultipleChoiceAction(pReader); + } + else if (pReader->ElementExists(ExitMultipleChoiceAction::GetNameForXml())) + { + return new ExitMultipleChoiceAction(pReader); + } + else if (pReader->ElementExists(EnableFastForwardAction::GetNameForXml())) + { + return new EnableFastForwardAction(pReader); + } + else if (pReader->ElementExists(DisableFastForwardAction::GetNameForXml())) + { + return new DisableFastForwardAction(pReader); + } + else if (pReader->ElementExists(BeginBreakdownAction::GetNameForXml())) + { + return new BeginBreakdownAction(pReader); + } + else if (pReader->ElementExists(EndBreakdownAction::GetNameForXml())) + { + return new EndBreakdownAction(pReader); + } + else if (pReader->ElementExists(Interrogation::InterrogationRepeatAction::GetNameForXml())) + { + return new Interrogation::InterrogationRepeatAction(pReader); + } + else if (pReader->ElementExists(Interrogation::ShowInterrogationAction::GetNameForXml())) + { + return new Interrogation::ShowInterrogationAction(pReader); + } + else if (pReader->ElementExists(Interrogation::ExitInterrogationRepeatAction::GetNameForXml())) + { + return new Interrogation::ExitInterrogationRepeatAction(pReader); + } + else if (pReader->ElementExists(Confrontation::ConfrontationTopicSelectionAction::GetNameForXml())) + { + return new Confrontation::ConfrontationTopicSelectionAction(pReader); + } + else if (pReader->ElementExists(Confrontation::EnableTopicAction::GetNameForXml())) + { + return new Confrontation::EnableTopicAction(pReader); + } + else if (pReader->ElementExists(Confrontation::RestartDecisionAction::GetNameForXml())) + { + return new Confrontation::RestartDecisionAction(pReader); + } + else if (pReader->ElementExists(Confrontation::RestartConfrontationAction::GetNameForXml())) + { + return new Confrontation::RestartConfrontationAction(pReader); + } + else + { + throw new MLIException("Unknown conversation action type."); + } +} + +QList *> Conversation::Action::GetListItemsForActions(QList &actions, int indentLevel) +{ + QList *> listItems; + + listItems.append(new TopSeparatorListItem(indentLevel, &actions, 0, true /* shouldInsert */)); + + if (actions.length() > 0) + { + int currentPosition = 0; + int itemCount = actions.length(); + + for (Conversation::Action *pAction : actions) + { + for (ListItemBase *pChildListItem : pAction->GetListItems(indentLevel)) + { + listItems.append(pChildListItem); + + if (pChildListItem->GetIndentLevel() == indentLevel) + { + pChildListItem->SetContainingObjectList(&actions, currentPosition, false /* shouldInsert */); + } + } + + currentPosition++; + + if (currentPosition != itemCount) + { + listItems.append(new MiddleSeparatorListItem(indentLevel, &actions, currentPosition, true /* shouldInsert */)); + } + } + + listItems.append(new BottomSeparatorListItem(indentLevel, &actions, itemCount, true /* shouldInsert */)); + } + + return listItems; +} + +QList *> Conversation::Action::GetListItems(int indentLevel) +{ + return GetListItemsImpl(indentLevel); +} + +void Conversation::Action::PushToDrawingView(Conversation::DrawingView *pDrawingView) +{ + pDrawingView->SetLeftCharacter( + stateDuringAction.GetLeftCharacterId(), + stateDuringAction.GetLeftCharacterEmotionId()); + + pDrawingView->SetRightCharacter( + stateDuringAction.GetRightCharacterId(), + stateDuringAction.GetRightCharacterEmotionId()); + + pDrawingView->SetDialog(CharacterPositionNone, QStringList(), "", false); +} + +Conversation::ActionWithNotification::ActionWithNotification() + : Action() +{ + shouldNotify = false; +} + +void Conversation::ActionWithNotification::AddNotification(Staging::Conversation::NotificationAction *pStagingNotificationAction) +{ + shouldNotify = true; + rawNotificationText = pStagingNotificationAction->RawNotificationText; + oldEvidenceId = pStagingNotificationAction->OldEvidenceId; + newEvidenceId = pStagingNotificationAction->NewEvidenceId; + partnerId = pStagingNotificationAction->PartnerId; + locationWithCutsceneId = pStagingNotificationAction->LocationWithCutsceneId; + cutsceneId = pStagingNotificationAction->CutsceneId; +} + +void Conversation::ActionWithNotification::CopyPropertiesImpl(Conversation::Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::ActionWithNotification *pAction = static_cast(pOther); + + pAction->shouldNotify = shouldNotify; + pAction->rawNotificationText = rawNotificationText; + pAction->oldEvidenceId = oldEvidenceId; + pAction->newEvidenceId = newEvidenceId; + pAction->partnerId = partnerId; + pAction->locationWithCutsceneId = locationWithCutsceneId; + pAction->cutsceneId = cutsceneId; +} + +Conversation::CharacterChangeAction::CharacterChangeAction() + : Action() +{ + position = CharacterPositionNone; +} + +Conversation::CharacterChangeAction::CharacterChangeAction(Staging::Conversation::CharacterChangeAction *pStagingCharacterChangeAction) + : Action() +{ + position = (CharacterPosition)(pStagingCharacterChangeAction->Position); + characterIdToChangeTo = pStagingCharacterChangeAction->CharacterIdToChangeTo; + initialEmotionId = pStagingCharacterChangeAction->InitialEmotionId; + sfxId = pStagingCharacterChangeAction->SfxId; +} + +void Conversation::CharacterChangeAction::WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) +{ + pWriter->StartElement("CharacterChangeAction"); + pWriter->WriteTextElement("Position", CharacterPositionToString(position)); + pWriter->WriteTextElement("CharacterIdToChangeTo", characterIdToChangeTo); + pWriter->WriteTextElement("InitialEmotionId", initialEmotionId); + + if (sfxId.length() > 0) + { + pWriter->WriteTextElement("SfxId", sfxId); + } + + pWriter->EndElement(); + + currentActionIndex++; +} + +void Conversation::CharacterChangeAction::UpdateAndCacheConversationState(State ¤tState) +{ + if (position == CharacterPositionLeft) + { + currentState.SetLeftCharacterId(characterIdToChangeTo); + currentState.SetLeftCharacterEmotionId(initialEmotionId); + } + else + { + currentState.SetRightCharacterId(characterIdToChangeTo); + currentState.SetRightCharacterEmotionId(initialEmotionId); + } + + Conversation::Action::UpdateAndCacheConversationState(currentState); +} + +EditorDialogContents * Conversation::CharacterChangeAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new CharacterChangeActionEditorDialogContents(stateBeforeObject, dynamic_cast(pActionToEdit)); +} + +void Conversation::CharacterChangeAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::CharacterChangeAction *pAction = static_cast(pOther); + + pAction->position = position; + pAction->characterIdToChangeTo = characterIdToChangeTo; + pAction->initialEmotionId = initialEmotionId; + pAction->sfxId = sfxId; +} + +QList *> Conversation::CharacterChangeAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new CharacterChangeActionListItem(this, indentLevel)); + return editorList; +} + +QString CharacterChangeActionListItem::GetDisplayString() +{ + QString displayString = "Change the "; + displayString += UnderlineString(CharacterPositionToString(pAction->position)); + displayString += " character to be "; + + Character *pCharacter = CaseContent::GetInstance()->GetById(pAction->characterIdToChangeTo); + + if (pCharacter != NULL) + { + displayString += UnderlineString(pCharacter->GetDisplayName()); + displayString += " with the emotion "; + displayString += UnderlineString(pAction->initialEmotionId); + } + else + { + displayString += UnderlineString("no character"); + } + + displayString += "."; + return displayString; +} + +CharacterChangeActionEditorDialogContents::CharacterChangeActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::CharacterChangeAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pCharacterChangeAction = static_cast(pActionToEdit->Clone()); + } + else + { + pCharacterChangeAction = new Conversation::CharacterChangeAction(); + } + + pObject = pCharacterChangeAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + QLabel *pLabel1 = new QLabel("Change the "); + pLayout->addWidget(pLabel1); + + QComboBox *pCharacterPositionComboBox = new QComboBox(); + pCharacterPositionComboBox->addItem("Left"); + pCharacterPositionComboBox->addItem("Right"); + pLayout->addWidget(pCharacterPositionComboBox); + + if (pActionToEdit != NULL) + { + pCharacterPositionComboBox->setCurrentIndex(pCharacterChangeAction->position == CharacterPositionRight ? 1 : 0); + } + + QLabel *pLabel2 = new QLabel(" character to be "); + pLayout->addWidget(pLabel2); + + CharacterSelector *pCharacterSelector = new CharacterSelector(true /* allowNoCharacter */); + pLayout->addWidget(pCharacterSelector); + + Character *pCharacter = NULL; + + if (pActionToEdit != NULL) + { + pCharacter = CaseContent::GetInstance()->GetById(pCharacterChangeAction->characterIdToChangeTo); + } + + pCharacterEmotionLabel = new QLabel(" with the emotion "); + pCharacterEmotionLabel->setGraphicsEffect(new QGraphicsOpacityEffect()); + pLayout->addWidget(pCharacterEmotionLabel); + + pCharacterEmotionComboBox = new QComboBox(); + pCharacterEmotionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + pLayout->addWidget(pCharacterEmotionComboBox); + + if (pCharacter != NULL) + { + currentCharacterId = pCharacter->GetId(); + pCharacterSelector->SetToCharacterById(pCharacter->GetId()); + + if (pCharacterSelector->GetSelectedCharacterId() > 0) + { + const QStringList emotionIds = pCharacter->GetEmotionIds(); + pCharacterEmotionComboBox->addItems(emotionIds); + + int indexOfCurrentEmotion = 0; + + if (pActionToEdit != NULL) + { + indexOfCurrentEmotion = emotionIds.indexOf(QRegExp(pCharacterChangeAction->initialEmotionId, Qt::CaseInsensitive)); + } + + if (indexOfCurrentEmotion >= 0) + { + pCharacterEmotionComboBox->setCurrentIndex(indexOfCurrentEmotion); + } + else + { + pCharacterEmotionComboBox->setCurrentText(pCharacter->GetDefaultEmotionId()); + } + } + else + { + SetOpacity(pCharacterEmotionLabel, 0.0); + SetOpacity(pCharacterEmotionComboBox, 0.0); + } + } + else + { + pCharacterSelector->SetToCharacterById(""); + + SetOpacity(pCharacterEmotionLabel, 0.0); + SetOpacity(pCharacterEmotionComboBox, 0.0); + } + + QObject::connect(pCharacterPositionComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(CharacterPositionComboBoxCurrentIndexChanged(QString))); + QObject::connect(pCharacterSelector, SIGNAL(CharacterSelected(QString)), this, SLOT(CharacterSelectorCharacterSelected(QString))); + QObject::connect(pCharacterEmotionComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(CharacterEmotionComboBoxCurrentIndexChanged(QString))); + + // Call these to initialize everything properly. + CharacterPositionComboBoxCurrentIndexChanged(pCharacterPositionComboBox->currentText()); + CharacterSelectorCharacterSelected(pCharacterSelector->GetSelectedCharacterId()); + CharacterEmotionComboBoxCurrentIndexChanged(pCharacterEmotionComboBox->currentText()); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool CharacterChangeActionEditorDialogContents::ValidateFields() +{ + return true; +} + +void CharacterChangeActionEditorDialogContents::CharacterPositionComboBoxCurrentIndexChanged(const QString& newPosition) +{ + pCharacterChangeAction->position = StringToCharacterPosition(newPosition); +} + +void CharacterChangeActionEditorDialogContents::CharacterSelectorCharacterSelected(const QString &newCharacterId) +{ + if (newCharacterId.length() > 0) + { + Character *pCharacter = CaseContent::GetInstance()->GetById(newCharacterId); + + if (pCharacter != NULL) + { + if (currentCharacterId != newCharacterId) + { + currentCharacterId = newCharacterId; + pCharacterChangeAction->characterIdToChangeTo = newCharacterId; + + const QStringList emotionIds = pCharacter->GetEmotionIds(); + pCharacterEmotionComboBox->clear(); + pCharacterEmotionComboBox->addItems(emotionIds); + + pCharacterEmotionComboBox->setCurrentText(pCharacter->GetDefaultEmotionId()); + } + + SetOpacity(pCharacterEmotionLabel, 1.0); + SetOpacity(pCharacterEmotionComboBox, 1.0); + } + else + { + pCharacterChangeAction->characterIdToChangeTo = ""; + + SetOpacity(pCharacterEmotionLabel, 0.0); + SetOpacity(pCharacterEmotionComboBox, 0.0); + } + } + else + { + pCharacterChangeAction->characterIdToChangeTo = ""; + + SetOpacity(pCharacterEmotionLabel, 0.0); + SetOpacity(pCharacterEmotionComboBox, 0.0); + } +} + +void CharacterChangeActionEditorDialogContents::CharacterEmotionComboBoxCurrentIndexChanged(const QString& newEmotionId) +{ + pCharacterChangeAction->initialEmotionId = newEmotionId; +} + +Conversation::SetFlagAction::SetFlagAction(Staging::Conversation::SetFlagAction *pStagingSetFlagAction) + : ActionWithNotification() +{ + flagId = pStagingSetFlagAction->FlagId; +} + +void Conversation::SetFlagAction::WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) +{ + pWriter->StartElement("SetFlagAction"); + pWriter->WriteTextElement("FlagId", flagId); + pWriter->EndElement(); + + currentActionIndex++; +} + +EditorDialogContents * Conversation::SetFlagAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new SetFlagActionEditorDialogContents(stateBeforeObject, dynamic_cast(pActionToEdit)); +} + +QList *> Conversation::SetFlagAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new SetFlagActionListItem(this, indentLevel)); + return editorList; +} + +void Conversation::SetFlagAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::SetFlagAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pOther, isForEdit); + pAction->flagId = flagId; +} + +QString SetFlagActionListItem::GetDisplayString() +{ + QString displayString = "Set the flag "; + displayString += UnderlineString(pAction->flagId); + displayString += " to true."; + return displayString; +} + +SetFlagActionEditorDialogContents::SetFlagActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::SetFlagAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pSetFlagAction = static_cast(pActionToEdit->Clone()); + } + else + { + pSetFlagAction = new Conversation::SetFlagAction(); + } + + pObject = pSetFlagAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + QLabel *pLabel1 = new QLabel("Set the flag "); + pLayout->addWidget(pLabel1); + + FlagEditor *pFlagEditor = new FlagEditor(); + + if (pActionToEdit != NULL) + { + pFlagEditor->SetToId(pActionToEdit->flagId); + } + + pLayout->addWidget(pFlagEditor); + + QLabel *pLabel2 = new QLabel(" to true."); + pLayout->addWidget(pLabel2); + + QObject::connect(pFlagEditor, SIGNAL(FlagSelected(QString)), this, SLOT(FlagEditorFlagSelected(QString))); + + // Call these to initialize everything properly. + FlagEditorFlagSelected(pFlagEditor->currentText()); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + setLayout(pLayout); +} + +bool SetFlagActionEditorDialogContents::ValidateFields() +{ + return true; +} + +void SetFlagActionEditorDialogContents::FlagEditorFlagSelected(const QString& newFlagId) +{ + pSetFlagAction->flagId = newFlagId; +} + +Conversation::BranchOnConditionAction::BranchOnConditionAction() + : Action() +{ + pCondition = NULL; +} + +Conversation::BranchOnConditionAction::BranchOnConditionAction(Staging::Conversation::BranchOnConditionAction *pStagingBranchOnConditionAction) + : Action() +{ + pCondition = new Condition(pStagingBranchOnConditionAction->pCondition); +} + +Conversation::BranchOnConditionAction::~BranchOnConditionAction() +{ + delete pCondition; + pCondition = NULL; + + for (Action *pAction : trueActions) + { + delete pAction; + } + + trueActions.clear(); + + for (Action *pAction : falseActions) + { + delete pAction; + } + + falseActions.clear(); +} + +void Conversation::BranchOnConditionAction::WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) +{ + pWriter->StartElement("BranchOnConditionAction"); + pWriter->StartElement("Condition"); + pCondition->WriteToCaseXml(pWriter); + pWriter->EndElement(); + pWriter->WriteIntElement("TrueIndex", currentActionIndex + 1); + pWriter->WriteIntElement("FalseIndex", currentActionIndex + trueActions.length() + 1); + pWriter->EndElement(); +} + +void Conversation::BranchOnConditionAction::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, trueActions); + Conversation::ReplaceAction(pNewAction, pOldAction, falseActions); +} + +void Conversation::BranchOnConditionAction::UpdateAndCacheConversationState(State ¤tState) +{ + Conversation::Action::UpdateAndCacheConversationState(currentState); + State trueActionsBranchState = currentState.Clone(); + State falseActionsBranchState = currentState.Clone(); + + for (Conversation::Action *pAction : trueActions) + { + pAction->UpdateAndCacheConversationState(trueActionsBranchState); + } + + for (Conversation::Action *pAction : falseActions) + { + pAction->UpdateAndCacheConversationState(falseActionsBranchState); + } + + currentState = trueActionsBranchState.Clone(); +} + +void Conversation::BranchOnConditionAction::PreloadAudio() +{ + for (Conversation::Action *pAction : trueActions) + { + pAction->PreloadAudio(); + } + + for (Conversation::Action *pAction : falseActions) + { + pAction->PreloadAudio(); + } +} + +EditorDialogContents * Conversation::BranchOnConditionAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new BranchOnConditionActionEditorDialogContents(stateBeforeObject, dynamic_cast(pActionToEdit)); +} + +void Conversation::BranchOnConditionAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::BranchOnConditionAction *pAction = static_cast(pOther); + + delete pAction->pCondition; + pAction->pCondition = pCondition->Clone(); + + Conversation::Action::CloneActionList(pAction->trueActions, trueActions); + Conversation::Action::CloneActionList(pAction->falseActions, falseActions); +} + +void Conversation::BranchOnConditionAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Conversation::BranchOnConditionAction *pAction = static_cast(pOther); + + Conversation::Action::ExchangeListItemBaseOwnership(pAction->trueActions, trueActions); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->falseActions, falseActions); +} + +QList *> Conversation::BranchOnConditionAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new BranchOnConditionActionListItem(this, indentLevel)); + editorList.append(new TextDisplay("If condition is true:", indentLevel + 1)); + editorList.append(GetListItemsForActions(trueActions, indentLevel + 2)); + editorList.append(new TextDisplay("If condition is false:", indentLevel + 1)); + editorList.append(GetListItemsForActions(falseActions, indentLevel + 2)); + + return editorList; +} + +QString BranchOnConditionActionListItem::GetDisplayString() +{ + QString displayString = "Branch on condition: "; + displayString += UnderlineString(pAction->pCondition->ToString()); + return displayString; +} + +BranchOnConditionActionEditorDialogContents::BranchOnConditionActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::BranchOnConditionAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pBranchOnConditionAction = static_cast(pActionToEdit->Clone()); + } + else + { + pBranchOnConditionAction = new Conversation::BranchOnConditionAction(); + } + + pObject = pBranchOnConditionAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Branch on condition:")); + pLayout->addSpacing(10); + + if (pActionToEdit != NULL) + { + pConditionEditor = pBranchOnConditionAction->pCondition->GetEditor(); + } + else + { + pConditionEditor = new ConditionEditor(); + } + + pLayout->addWidget(pConditionEditor); + + pLayout->setSpacing(0); + pLayout->setContentsMargins(0, 0, 0, 0); + + setLayout(pLayout); +} + +bool BranchOnConditionActionEditorDialogContents::ValidateFields() +{ + return true; +} + +void BranchOnConditionActionEditorDialogContents::FinalizeObject() +{ + delete pBranchOnConditionAction->pCondition; + pBranchOnConditionAction->pCondition = pConditionEditor->GetCondition(); +} + +Conversation::ShowDialogAction::ShowDialogAction() + : Action() +{ + speakerPosition = CharacterPositionNone; + leadInTime = 0; + shouldAdvanceAutomatically = false; + delayBeforeContinuing = 0; +} + +Conversation::ShowDialogAction::ShowDialogAction(Staging::Conversation::ShowDialogAction *pStagingShowDialogAction) + : ShowDialogAction() +{ + speakerPosition = (CharacterPosition)pStagingShowDialogAction->SpeakerPosition; + characterId = pStagingShowDialogAction->CharacterId; + rawDialog = pStagingShowDialogAction->RawDialog; + voiceOverFilePath = "Audio/" + pStagingShowDialogAction->FilePath; + leadInTime = pStagingShowDialogAction->LeadInTime; + + if (pStagingShowDialogAction->GetType() == Staging::ConversationActionType_ShowDialogAutomatic) + { + Staging::Conversation::ShowDialogAutomaticAction *pStagingShowDialogAutomaticAction = + static_cast(pStagingShowDialogAction); + + shouldAdvanceAutomatically = true; + delayBeforeContinuing = pStagingShowDialogAutomaticAction->DelayBeforeContinuing; + } + + InitializeDialog(); +} + +void Conversation::ShowDialogAction::InitializeDialog() +{ + parsedDialog.Reset(); + parsedDialog.SetParsedDialog(ParseRawDialog(this, rawDialog, dialogTextArea, dialogPadding, QFont("FayesMousewriting", 28))); + parsedDialog.SetAudio(voiceOverFilePath); +} + +void Conversation::ShowDialogAction::UpdateAndCacheConversationState(State ¤tState) +{ + stateBeforeAction = currentState.Clone(); + + QRegularExpression eventsRegularExpression("{(.*?)}"); + QRegularExpressionMatchIterator eventsIterator = eventsRegularExpression.globalMatch(rawDialog); + + while (eventsIterator.hasNext()) + { + QRegularExpressionMatch eventMatch = eventsIterator.next(); + QString eventString = eventMatch.captured(1); + QStringList eventComponents = eventString.split(':'); + + if (eventComponents.size() == 2) + { + if (eventComponents[0].toLower() == "emotion") + { + if (speakerPosition == CharacterPositionLeft) + { + currentState.SetLeftCharacterEmotionId(eventComponents[1]); + } + else + { + currentState.SetRightCharacterEmotionId(eventComponents[1]); + } + } + else if (eventComponents[0].toLower() == "otheremotion") + { + if (speakerPosition == CharacterPositionLeft) + { + currentState.SetRightCharacterEmotionId(eventComponents[1]); + } + else + { + currentState.SetLeftCharacterEmotionId(eventComponents[1]); + } + } + } + } + + Conversation::Action::UpdateAndCacheConversationState(currentState); + + parsedDialog.SetCharacters( + stateBeforeAction.GetLeftCharacterId(), + stateBeforeAction.GetLeftCharacterEmotionId(), + stateBeforeAction.GetRightCharacterId(), + stateBeforeAction.GetRightCharacterEmotionId(), + speakerPosition, + characterId); +} + +void Conversation::ShowDialogAction::PreloadAudio() +{ + parsedDialog.PreloadAudio(); +} + +void Conversation::ShowDialogAction::PushToDrawingView(Conversation::DrawingView *pDrawingView) +{ + Conversation::Action::PushToDrawingView(pDrawingView); + pDrawingView->SetDialog(speakerPosition, parsedDialog.GetHtmlLines(), characterId, false); +} + +void Conversation::ShowDialogAction::PlayOnDrawingView(Conversation::DrawingView *pDrawingView) +{ + parsedDialog.PlayOnDrawingView(pDrawingView); +} + +void Conversation::ShowDialogAction::StopPlaying() +{ + parsedDialog.StopPlaying(); +} + +EditorDialogContents * Conversation::ShowDialogAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ShowDialogActionEditorDialogContents(stateBeforeObject, ShowDialogActionEditorDialogContents::Type::ShowDialog, dynamic_cast(pActionToEdit)); +} + +void Conversation::ShowDialogAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::ShowDialogAction *pAction = static_cast(pOther); + + pAction->speakerPosition = speakerPosition; + pAction->characterId = characterId; + pAction->rawDialog = rawDialog; + pAction->voiceOverFilePath = voiceOverFilePath; + pAction->leadInTime = leadInTime; + pAction->shouldAdvanceAutomatically = shouldAdvanceAutomatically; + pAction->delayBeforeContinuing = delayBeforeContinuing; + pAction->InitializeDialog(); +} + +QList *> Conversation::ShowDialogAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new ShowDialogActionListItem(this, indentLevel)); + return editorList; +} + +QString ShowDialogActionListItem::GetDisplayString() +{ + QString speakerName; + + Character *pLeftCharacter = CaseContent::GetInstance()->GetById(stateBeforeObject.GetLeftCharacterId()); + Character *pRightCharacter = CaseContent::GetInstance()->GetById(stateBeforeObject.GetRightCharacterId()); + + switch (pAction->speakerPosition) + { + case CharacterPositionLeft: + speakerName = (pLeftCharacter != NULL ? pLeftCharacter->GetDisplayName() : "no one") + " (the left character)"; + break; + case CharacterPositionRight: + speakerName = (pRightCharacter != NULL ? pRightCharacter->GetDisplayName() : "no one") + " (the right character)"; + break; + case CharacterPositionOffscreen: + speakerName = CaseContent::GetInstance()->GetById(pAction->characterId)->GetDisplayName() + " (offscreen)"; + break; + case CharacterPositionUnknown: + speakerName = "an unseen character"; + break; + default: + throw new MLIException("Unknown speaker position."); + } + + QString displayString = GetHeaderString(speakerName); + displayString += "

\"" + StripDialogEvents(pAction->rawDialog) + "\"

"; + + if (pAction->voiceOverFilePath.length() > 0) + { + displayString += "

With associated voice-over file: " + pAction->voiceOverFilePath + ".ogg

"; + } + + if (pAction->leadInTime > 0 && pAction->shouldAdvanceAutomatically) + { + displayString += "

Wait "; + displayString += UnderlineString(QString::number(pAction->leadInTime) + " milliseconds"); + displayString += " before beginning the line, and then wait "; + displayString += UnderlineString(QString::number(pAction->delayBeforeContinuing) + " milliseconds"); + displayString += " before automatically continuing.

"; + } + else if (pAction->leadInTime > 0) + { + displayString += "

Wait "; + displayString += UnderlineString(QString::number(pAction->leadInTime) + " milliseconds"); + displayString += " before beginning the line.

"; + } + else if (pAction->shouldAdvanceAutomatically) + { + displayString += "

Wait "; + displayString += UnderlineString(QString::number(pAction->delayBeforeContinuing) + " milliseconds"); + displayString += " before automatically continuing.

"; + } + + displayString += GetFooterString(); + return displayString; +} + +QString ShowDialogActionListItem::GetHeaderString(const QString &speakerName) +{ + QString headerString = "

Show "; + headerString += UnderlineString(speakerName); + headerString += " saying this:

"; + + return headerString; +} + +QString ShowDialogActionListItem::GetFooterString() +{ + return ""; +} + +ShowDialogActionEditorDialogContents::ShowDialogActionEditorDialogContents(const Conversation::State &stateBeforeObject, ShowDialogActionEditorDialogContents::Type type, Conversation::ShowDialogAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pShowDialogAction = static_cast(pActionToEdit->Clone()); + + switch (type) + { + case ShowDialogActionEditorDialogContents::Type::ShowInterrogation: + { + Interrogation::ShowInterrogationAction *pShowInterrogationAction = static_cast(pActionToEdit); + + for (QString evidenceId : pShowInterrogationAction->evidencePresentedActionsByEvidenceId.keys()) + { + GetShowInterrogationAction()->originalEvidenceIdList.append(evidenceId); + GetShowInterrogationAction()->originalEvidenceIdToNewEvidenceIdMap.insert(evidenceId, evidenceId); + } + + for (QString partnerId : pShowInterrogationAction->partnerUsedActionsByPartnerId.keys()) + { + GetShowInterrogationAction()->originalPartnerIdList.append(partnerId); + GetShowInterrogationAction()->originalPartnerIdToNewPartnerIdMap.insert(partnerId, partnerId); + } + } + + break; + + default: + break; + } + } + else + { + switch (type) + { + case ShowDialogActionEditorDialogContents::Type::ShowDialog: + pShowDialogAction = new Conversation::ShowDialogAction(); + break; + + case ShowDialogActionEditorDialogContents::Type::MustPresentEvidence: + pShowDialogAction = new Conversation::MustPresentEvidenceAction(); + break; + + case ShowDialogActionEditorDialogContents::Type::ShowInterrogation: + pShowDialogAction = new Interrogation::ShowInterrogationAction(); + break; + + default: + throw new MLIException("Unknown action type."); + } + } + + pObject = pShowDialogAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + QHBoxLayout *pSpeakerLayout = new QHBoxLayout(); + + QLabel *pSpeakerLabelStart = new QLabel("Show "); + pSpeakerLayout->addWidget(pSpeakerLabelStart); + + QComboBox *pSpeakerComboBox = new QComboBox(); + + Character *pLeftCharacter = CaseContent::GetInstance()->GetById(this->stateBeforeObject.GetLeftCharacterId()); + Character *pRightCharacter = CaseContent::GetInstance()->GetById(this->stateBeforeObject.GetRightCharacterId()); + + pSpeakerComboBox->addItem((pLeftCharacter != NULL ? pLeftCharacter->GetDisplayName() : "no one") + " (the left character)"); + pSpeakerComboBox->addItem((pRightCharacter != NULL ? pRightCharacter->GetDisplayName() : "no one") + " (the right character)"); + pSpeakerComboBox->addItem("an offscreen character..."); + pSpeakerComboBox->addItem("an unseen character"); + pSpeakerLayout->addWidget(pSpeakerComboBox); + + if (pActionToEdit != NULL) + { + switch (pActionToEdit->speakerPosition) + { + case CharacterPositionLeft: + pSpeakerComboBox->setCurrentIndex(0); + break; + case CharacterPositionRight: + pSpeakerComboBox->setCurrentIndex(1); + break; + case CharacterPositionOffscreen: + pSpeakerComboBox->setCurrentIndex(2); + break; + case CharacterPositionUnknown: + pSpeakerComboBox->setCurrentIndex(3); + break; + default: + throw new MLIException("Unknown speaker position."); + } + } + + pOffscreenCharacterSelector = new CharacterSelector(false /* allowNoCharacter */); + pSpeakerLayout->addWidget(pOffscreenCharacterSelector); + + QLabel *pSpeakerLabelEnd = new QLabel(" saying this:"); + pSpeakerLayout->addWidget(pSpeakerLabelEnd); + + pSpeakerLayout->addStretch(1); + pLayout->addLayout(pSpeakerLayout); + + pRawDialogTextEdit = new QPlainTextEdit(); + pRawDialogTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + pRawDialogTextEdit->setSizeAdjustPolicy(QPlainTextEdit::AdjustToContents); + pLayout->addWidget(pRawDialogTextEdit); + + if (pActionToEdit != NULL) + { + pRawDialogTextEdit->setPlainText(pActionToEdit->rawDialog); + } + + QHBoxLayout *pVoiceOverFileLayout = new QHBoxLayout(); + + QCheckBox *pVoiceOverCheckBox = new QCheckBox("Associate voice-over file... "); + pVoiceOverFileLayout->addWidget(pVoiceOverCheckBox); + + pSelectVoiceOverFilePushButton = new QPushButton("Select file"); + pVoiceOverFileLayout->addWidget(pSelectVoiceOverFilePushButton); + + pVoiceOverFilePathLabel = new QLabel(); + pVoiceOverFilePathLabel->setMinimumWidth(300); + pVoiceOverFileLayout->addWidget(pVoiceOverFilePathLabel); + + if (pActionToEdit != NULL) + { + pVoiceOverCheckBox->setChecked(pActionToEdit->voiceOverFilePath.length() > 0); + pVoiceOverFilePathLabel->setText(pActionToEdit->voiceOverFilePath + ".ogg"); + } + + pVoiceOverFileLayout->addStretch(1); + pLayout->addLayout(pVoiceOverFileLayout); + + QHBoxLayout *pLeadInTimeLayout = new QHBoxLayout(); + + QCheckBox *pLeadInTimeCheckBox = new QCheckBox("Wait before starting audio playback... "); + pLeadInTimeLayout->addWidget(pLeadInTimeCheckBox); + + pLeadInTimeAmountLineEdit = new QLineEdit(); + pLeadInTimeAmountLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9]+"))); + pLeadInTimeLayout->addWidget(pLeadInTimeAmountLineEdit); + + pLeadInTimeMillisecondsLabel = new QLabel(" milliseconds"); + pLeadInTimeLayout->addWidget(pLeadInTimeMillisecondsLabel); + + if (pActionToEdit != NULL) + { + pLeadInTimeCheckBox->setChecked(pActionToEdit->leadInTime > 0); + pLeadInTimeAmountLineEdit->setText(QString::number(pActionToEdit->leadInTime)); + } + else + { + pLeadInTimeAmountLineEdit->setText("0"); + } + + pLeadInTimeLayout->addStretch(1); + pLayout->addLayout(pLeadInTimeLayout); + + QHBoxLayout *pAutomaticallyContinueLayout = new QHBoxLayout(); + + QCheckBox *pAutomaticallyContinueCheckBox = new QCheckBox("Automatically continue after time elapses... "); + pAutomaticallyContinueLayout->addWidget(pAutomaticallyContinueCheckBox); + + pAutomaticallyContinueAmountLineEdit = new QLineEdit(); + pAutomaticallyContinueAmountLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9]+"))); + pAutomaticallyContinueLayout->addWidget(pAutomaticallyContinueAmountLineEdit); + + pAutomaticallyContinueMillisecondsLabel = new QLabel(" milliseconds"); + pAutomaticallyContinueLayout->addWidget(pAutomaticallyContinueMillisecondsLabel); + + if (pActionToEdit != NULL) + { + pAutomaticallyContinueCheckBox->setChecked(pActionToEdit->shouldAdvanceAutomatically); + pAutomaticallyContinueAmountLineEdit->setText(QString::number(pActionToEdit->delayBeforeContinuing)); + } + else + { + pAutomaticallyContinueAmountLineEdit->setText("0"); + } + + pAutomaticallyContinueLayout->addStretch(1); + pLayout->addLayout(pAutomaticallyContinueLayout); + + QObject::connect(pSpeakerComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SpeakerComboBoxCurrentIndexChanged(int))); + QObject::connect(pOffscreenCharacterSelector, SIGNAL(CharacterSelected(QString)), this, SLOT(OffscreenCharacterSelectorCharacterSelected(QString))); + QObject::connect(pRawDialogTextEdit, SIGNAL(textChanged()), this, SLOT(RawDialogTextEditTextChanged())); + QObject::connect(pVoiceOverCheckBox, SIGNAL(toggled(bool)), this, SLOT(VoiceOverCheckBoxToggled(bool))); + QObject::connect(pSelectVoiceOverFilePushButton, SIGNAL(clicked()), this, SLOT(SelectVoiceOverFilePushButtonClicked())); + QObject::connect(pLeadInTimeCheckBox, SIGNAL(toggled(bool)), this, SLOT(LeadInTimeCheckBoxToggled(bool))); + QObject::connect(pLeadInTimeAmountLineEdit, SIGNAL(editingFinished()), this, SLOT(LeadInTimeAmountLineEditEditingFinished())); + QObject::connect(pAutomaticallyContinueCheckBox, SIGNAL(toggled(bool)), this, SLOT(AutomaticallyContinueCheckBoxToggled(bool))); + QObject::connect(pAutomaticallyContinueAmountLineEdit, SIGNAL(editingFinished()), this, SLOT(AutomaticallyContinueAmountLineEditEditingFinished())); + + // Call these to initialize everything properly. + SpeakerComboBoxCurrentIndexChanged(pSpeakerComboBox->currentIndex()); + OffscreenCharacterSelectorCharacterSelected(pOffscreenCharacterSelector->GetSelectedCharacterId()); + VoiceOverCheckBoxToggled(pVoiceOverCheckBox->isChecked()); + LeadInTimeCheckBoxToggled(pLeadInTimeCheckBox->isChecked()); + AutomaticallyContinueCheckBoxToggled(pAutomaticallyContinueCheckBox->isChecked()); + + if (type == ShowDialogActionEditorDialogContents::Type::MustPresentEvidence) + { + QLabel *pAcceptedEvidenceLabel = new QLabel(); + pAcceptedEvidenceLabel->setText("Require the player to present one of these pieces of evidence:"); + pLayout->addWidget(pAcceptedEvidenceLabel); + + MultipleSelectionWidget *pEvidenceSelector = new MultipleSelectionWidget(); + + QObject::connect(pEvidenceSelector, SIGNAL(StringChanged(int,QString)), this, SLOT(EvidenceSelectorStringChanged(int,QString))); + QObject::connect(pEvidenceSelector, SIGNAL(StringAdded(int,QString)), this, SLOT(EvidenceSelectorStringAdded(int,QString))); + QObject::connect(pEvidenceSelector, SIGNAL(StringRemoved(int)), this, SLOT(EvidenceSelectorStringRemoved(int))); + + Conversation::MustPresentEvidenceAction *pMustPresentEvidenceAction = GetMustPresentEvidenceAction(); + + if (pMustPresentEvidenceAction != NULL && pMustPresentEvidenceAction->correctEvidenceIdList.size() > 0) + { + pEvidenceSelector->SetSelections(pMustPresentEvidenceAction->correctEvidenceIdList); + } + else + { + QStringList stringList; + stringList.append(""); + + pEvidenceSelector->SetSelections(stringList); + } + + pLayout->addWidget(pEvidenceSelector); + } + else if (type == ShowDialogActionEditorDialogContents::Type::ShowInterrogation) + { + Interrogation::ShowInterrogationAction *pShowInterrogationAction = GetShowInterrogationAction(); + + QCheckBox *pAcceptEvidenceCheckBox = new QCheckBox("Allow the player to present evidence here"); + pLayout->addWidget(pAcceptEvidenceCheckBox); + + QObject::connect(pAcceptEvidenceCheckBox, SIGNAL(toggled(bool)), this, SLOT(InterrogationAcceptEvidenceCheckBoxToggled(bool))); + + pAcceptedEvidenceSelector = new MultipleSelectionWidget(); + pLayout->addWidget(pAcceptedEvidenceSelector); + + QObject::connect(pAcceptedEvidenceSelector, SIGNAL(StringChanged(int,QString)), this, SLOT(InterrogationEvidenceSelectorStringChanged(int,QString))); + QObject::connect(pAcceptedEvidenceSelector, SIGNAL(StringAdded(int,QString)), this, SLOT(InterrogationEvidenceSelectorStringAdded(int,QString))); + QObject::connect(pAcceptedEvidenceSelector, SIGNAL(StringRemoved(int)), this, SLOT(InterrogationEvidenceSelectorStringRemoved(int))); + + if (pShowInterrogationAction->evidencePresentedActionsByEvidenceId.keys().size() > 0) + { + pAcceptEvidenceCheckBox->setChecked(true); + pAcceptedEvidenceSelector->SetSelections(pShowInterrogationAction->evidencePresentedActionsByEvidenceId.keys()); + } + else + { + QStringList stringList; + stringList.append(""); + + pAcceptEvidenceCheckBox->setChecked(false); + pAcceptedEvidenceSelector->SetSelections(stringList); + pAcceptedEvidenceSelector->hide(); + } + + acceptedEvidenceList.append(pAcceptedEvidenceSelector->GetSelections()); + + QCheckBox *pAcceptPartnersCheckBox = new QCheckBox("Allow the player to use a partner here"); + pLayout->addWidget(pAcceptPartnersCheckBox); + + QObject::connect(pAcceptPartnersCheckBox, SIGNAL(toggled(bool)), this, SLOT(InterrogationAcceptPartnerCheckBoxToggled(bool))); + + pAcceptedPartnersSelector = new MultipleSelectionWidget(); + pLayout->addWidget(pAcceptedPartnersSelector); + + QObject::connect(pAcceptedPartnersSelector, SIGNAL(StringChanged(int,QString)), this, SLOT(InterrogationPartnerSelectorStringChanged(int,QString))); + QObject::connect(pAcceptedPartnersSelector, SIGNAL(StringAdded(int,QString)), this, SLOT(InterrogationPartnerSelectorStringAdded(int,QString))); + QObject::connect(pAcceptedPartnersSelector, SIGNAL(StringRemoved(int)), this, SLOT(InterrogationPartnerSelectorStringRemoved(int))); + + if (pShowInterrogationAction->partnerUsedActionsByPartnerId.keys().size() > 0) + { + pAcceptPartnersCheckBox->setChecked(true); + pAcceptedPartnersSelector->SetSelections(pShowInterrogationAction->partnerUsedActionsByPartnerId.keys()); + } + else + { + QStringList stringList; + stringList.append(""); + + pAcceptPartnersCheckBox->setChecked(false); + pAcceptedPartnersSelector->SetSelections(stringList); + pAcceptedPartnersSelector->hide(); + } + + acceptedPartnerList.append(pAcceptedPartnersSelector->GetSelections()); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool ShowDialogActionEditorDialogContents::ValidateFields() +{ + return true; +} + +void ShowDialogActionEditorDialogContents::OnAccepted() +{ + pShowDialogAction->InitializeDialog(); + pShowDialogAction->PreloadAudio(); +} + +void ShowDialogActionEditorDialogContents::SpeakerComboBoxCurrentIndexChanged(int newIndex) +{ + switch (newIndex) + { + case 0: + pShowDialogAction->speakerPosition = CharacterPositionLeft; + pOffscreenCharacterSelector->hide(); + break; + case 1: + pShowDialogAction->speakerPosition = CharacterPositionRight; + pOffscreenCharacterSelector->hide(); + break; + case 2: + pShowDialogAction->speakerPosition = CharacterPositionOffscreen; + pOffscreenCharacterSelector->show(); + break; + case 3: + pShowDialogAction->speakerPosition = CharacterPositionUnknown; + pOffscreenCharacterSelector->hide(); + break; + default: + throw new MLIException("Unexpected SpeakerComboBox index selected."); + } +} + +void ShowDialogActionEditorDialogContents::OffscreenCharacterSelectorCharacterSelected(const QString &newCharacterId) +{ + pShowDialogAction->characterId = newCharacterId; +} + +void ShowDialogActionEditorDialogContents::RawDialogTextEditTextChanged() +{ + pShowDialogAction->rawDialog = pRawDialogTextEdit->toPlainText(); +} + +void ShowDialogActionEditorDialogContents::VoiceOverCheckBoxToggled(bool isChecked) +{ + if (isChecked) + { + SetOpacity(pSelectVoiceOverFilePushButton, 1.0); + SetOpacity(pVoiceOverFilePathLabel, 1.0); + } + else + { + SetOpacity(pSelectVoiceOverFilePushButton, 0.0); + SetOpacity(pVoiceOverFilePathLabel, 0.0); + } +} + +void ShowDialogActionEditorDialogContents::SelectVoiceOverFilePushButtonClicked() +{ + QString filePath = QFileDialog::getOpenFileName(this, tr("Select voice-over file"), QString(), tr("Voice-over files (*.ogg)")); + + if (filePath.length() > 0) + { + filePath = CaseContent::GetInstance()->AbsolutePathToRelativePath(filePath); + pVoiceOverFilePathLabel->setText(filePath); + + // Remove the ".ogg" from the stored file path - that's implied. + pShowDialogAction->voiceOverFilePath = filePath.left(filePath.length() - 4); + } +} + +void ShowDialogActionEditorDialogContents::LeadInTimeCheckBoxToggled(bool isChecked) +{ + if (isChecked) + { + SetOpacity(pLeadInTimeAmountLineEdit, 1.0); + SetOpacity(pLeadInTimeMillisecondsLabel, 1.0); + } + else + { + SetOpacity(pLeadInTimeAmountLineEdit, 0.0); + SetOpacity(pLeadInTimeMillisecondsLabel, 0.0); + } +} + +void ShowDialogActionEditorDialogContents::LeadInTimeAmountLineEditEditingFinished() +{ + pShowDialogAction->leadInTime = pLeadInTimeAmountLineEdit->text().toInt(); +} + +void ShowDialogActionEditorDialogContents::AutomaticallyContinueCheckBoxToggled(bool isChecked) +{ + pShowDialogAction->shouldAdvanceAutomatically = isChecked; + + if (isChecked) + { + SetOpacity(pAutomaticallyContinueAmountLineEdit, 1.0); + SetOpacity(pAutomaticallyContinueMillisecondsLabel, 1.0); + } + else + { + SetOpacity(pAutomaticallyContinueAmountLineEdit, 0.0); + SetOpacity(pAutomaticallyContinueMillisecondsLabel, 0.0); + } +} + +void ShowDialogActionEditorDialogContents::AutomaticallyContinueAmountLineEditEditingFinished() +{ + pShowDialogAction->leadInTime = pAutomaticallyContinueAmountLineEdit->text().toInt(); +} + +void ShowDialogActionEditorDialogContents::EvidenceSelectorStringChanged(int index, const QString &string) +{ + GetMustPresentEvidenceAction()->correctEvidenceIdList[index] = string; +} + +void ShowDialogActionEditorDialogContents::EvidenceSelectorStringAdded(int index, const QString &string) +{ + GetMustPresentEvidenceAction()->correctEvidenceIdList.insert(index, string); +} + +void ShowDialogActionEditorDialogContents::EvidenceSelectorStringRemoved(int index) +{ + GetMustPresentEvidenceAction()->correctEvidenceIdList.removeAt(index); +} + +void ShowDialogActionEditorDialogContents::InterrogationAcceptEvidenceCheckBoxToggled(bool isChecked) +{ + if (isChecked) + { + pAcceptedEvidenceSelector->show(); + + for (const QString &string : pAcceptedEvidenceSelector->GetSelections()) + { + GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId.insert(string, QList()); + } + } + else + { + pAcceptedEvidenceSelector->hide(); + GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId.clear(); + } +} + +void ShowDialogActionEditorDialogContents::InterrogationEvidenceSelectorStringChanged(int index, const QString &string) +{ + QString previousString = acceptedEvidenceList[index]; + acceptedEvidenceList[index] = string; + + GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId[string] = GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId[previousString]; + GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId.remove(previousString); + + GetShowInterrogationAction()->originalEvidenceIdToNewEvidenceIdMap[GetShowInterrogationAction()->originalEvidenceIdList[index]] = string; +} + +void ShowDialogActionEditorDialogContents::InterrogationEvidenceSelectorStringAdded(int index, const QString &string) +{ + acceptedEvidenceList.insert(index, string); + + GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId.insert(string, QList()); +} + +void ShowDialogActionEditorDialogContents::InterrogationEvidenceSelectorStringRemoved(int index) +{ + QString previousString = acceptedEvidenceList[index]; + acceptedEvidenceList.removeAt(index); + + GetShowInterrogationAction()->evidencePresentedActionsByEvidenceId.remove(previousString); +} + +void ShowDialogActionEditorDialogContents::InterrogationAcceptPartnerCheckBoxToggled(bool isChecked) +{ + if (isChecked) + { + pAcceptedPartnersSelector->show(); + + for (const QString &string : pAcceptedPartnersSelector->GetSelections()) + { + GetShowInterrogationAction()->partnerUsedActionsByPartnerId.insert(string, QList()); + } + } + else + { + pAcceptedPartnersSelector->hide(); + GetShowInterrogationAction()->partnerUsedActionsByPartnerId.clear(); + } +} + +void ShowDialogActionEditorDialogContents::InterrogationPartnerSelectorStringChanged(int index, const QString &string) +{ + QString previousString = acceptedPartnerList[index]; + acceptedPartnerList[index] = string; + + GetShowInterrogationAction()->partnerUsedActionsByPartnerId[string] = GetShowInterrogationAction()->partnerUsedActionsByPartnerId[previousString]; + GetShowInterrogationAction()->partnerUsedActionsByPartnerId.remove(previousString); + + GetShowInterrogationAction()->originalPartnerIdToNewPartnerIdMap[GetShowInterrogationAction()->originalPartnerIdList[index]] = string; +} + +void ShowDialogActionEditorDialogContents::InterrogationPartnerSelectorStringAdded(int index, const QString &string) +{ + acceptedPartnerList.insert(index, string); + + GetShowInterrogationAction()->partnerUsedActionsByPartnerId.insert(string, QList()); +} + +void ShowDialogActionEditorDialogContents::InterrogationPartnerSelectorStringRemoved(int index) +{ + QString previousString = acceptedPartnerList[index]; + acceptedPartnerList.removeAt(index); + + GetShowInterrogationAction()->partnerUsedActionsByPartnerId.remove(previousString); +} + +Conversation::MustPresentEvidenceAction::MustPresentEvidenceAction() + : ShowDialogAction() +{ + canEndBeRequested = false; +} + +Conversation::MustPresentEvidenceAction::MustPresentEvidenceAction(Staging::Conversation::MustPresentEvidenceAction *pStagingMustPresentEvidenceAction) + : ShowDialogAction(pStagingMustPresentEvidenceAction) +{ + for (QString evidenceId : pStagingMustPresentEvidenceAction->CorrectEvidenceIdList) + { + correctEvidenceIdList.append(evidenceId); + } + + canEndBeRequested = pStagingMustPresentEvidenceAction->EndRequestedIndex >= 0; +} + +Conversation::MustPresentEvidenceAction::~MustPresentEvidenceAction() +{ + for (Action *pAction : correctEvidencePresentedActions) + { + delete pAction; + } + + correctEvidencePresentedActions.clear(); + + for (Action *pAction : wrongEvidencePresentedActions) + { + delete pAction; + } + + wrongEvidencePresentedActions.clear(); + + for (Action *pAction : endRequestedActions) + { + delete pAction; + } + + endRequestedActions.clear(); +} + +void Conversation::MustPresentEvidenceAction::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, correctEvidencePresentedActions); + Conversation::ReplaceAction(pNewAction, pOldAction, wrongEvidencePresentedActions); + Conversation::ReplaceAction(pNewAction, pOldAction, endRequestedActions); +} + +void Conversation::MustPresentEvidenceAction::UpdateAndCacheConversationState(State ¤tState) +{ + Conversation::ShowDialogAction::UpdateAndCacheConversationState(currentState); + + State correctActionsBranchState = currentState.Clone(); + State wrongActionsBranchState = currentState.Clone(); + State endRequestedActionsBranchState = currentState.Clone(); + + for (Conversation::Action *pAction : correctEvidencePresentedActions) + { + pAction->UpdateAndCacheConversationState(correctActionsBranchState); + } + + for (Conversation::Action *pAction : wrongEvidencePresentedActions) + { + pAction->UpdateAndCacheConversationState(wrongActionsBranchState); + } + + for (Conversation::Action *pAction : endRequestedActions) + { + pAction->UpdateAndCacheConversationState(endRequestedActionsBranchState); + } + + currentState = correctActionsBranchState.Clone(); +} + +void Conversation::MustPresentEvidenceAction::PreloadAudio() +{ + Conversation::ShowDialogAction::PreloadAudio(); + + for (Conversation::Action *pAction : correctEvidencePresentedActions) + { + pAction->PreloadAudio(); + } + + for (Conversation::Action *pAction : wrongEvidencePresentedActions) + { + pAction->PreloadAudio(); + } + + for (Conversation::Action *pAction : endRequestedActions) + { + pAction->PreloadAudio(); + } +} + +EditorDialogContents * Conversation::MustPresentEvidenceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ShowDialogActionEditorDialogContents(stateBeforeObject, ShowDialogActionEditorDialogContents::Type::MustPresentEvidence, dynamic_cast(pActionToEdit)); +} + +void Conversation::MustPresentEvidenceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::MustPresentEvidenceAction *pAction = static_cast(pOther); + + Conversation::ShowDialogAction::CopyPropertiesImpl(pAction, isForEdit); + + pAction->correctEvidenceIdList.clear(); + + for (QString evidenceId : correctEvidenceIdList) + { + pAction->correctEvidenceIdList.push_back(evidenceId); + } + + Conversation::Action::CloneActionList(pAction->correctEvidencePresentedActions, correctEvidencePresentedActions); + Conversation::Action::CloneActionList(pAction->wrongEvidencePresentedActions, wrongEvidencePresentedActions); + Conversation::Action::CloneActionList(pAction->endRequestedActions, endRequestedActions); + + pAction->canEndBeRequested = canEndBeRequested; +} + +void Conversation::MustPresentEvidenceAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Conversation::MustPresentEvidenceAction *pAction = static_cast(pOther); + + Conversation::Action::ExchangeListItemBaseOwnership(pAction->correctEvidencePresentedActions, correctEvidencePresentedActions); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->wrongEvidencePresentedActions, wrongEvidencePresentedActions); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->endRequestedActions, endRequestedActions); +} + +QList *> Conversation::MustPresentEvidenceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new MustPresentEvidenceActionListItem(this, indentLevel)); + editorList.append(new TextDisplay("If correct evidence is presented:", indentLevel + 1)); + editorList.append(GetListItemsForActions(correctEvidencePresentedActions, indentLevel + 2)); + editorList.append(new TextDisplay("If incorrect evidence is presented:", indentLevel + 1)); + editorList.append(GetListItemsForActions(wrongEvidencePresentedActions, indentLevel + 2)); + editorList.append(new TextDisplay("If the player gives up:", indentLevel + 1)); + editorList.append(GetListItemsForActions(endRequestedActions, indentLevel + 2)); + + return editorList; +} + +QString MustPresentEvidenceActionListItem::GetHeaderString(const QString &speakerName) +{ + QString headerString = "

Require the player to present evidence after showing "; + headerString += UnderlineString(speakerName); + headerString += " saying this:

"; + + return headerString; +} + +QString MustPresentEvidenceActionListItem::GetFooterString() +{ + Conversation::MustPresentEvidenceAction *pMustPresentEvidenceAction = GetMustPresentEvidenceAction(); + QString footerString = "

Accept the following evidence as correct: " + ConcatenateStringList(pMustPresentEvidenceAction->correctEvidenceIdList) + "

"; + + if (pMustPresentEvidenceAction->canEndBeRequested) + { + footerString += "

Allow the player to exit without correctly presenting evidence.

"; + } + + return footerString; +} + +Conversation::EnableConversationAction::EnableConversationAction(Staging::Conversation::EnableConversationAction *pStagingEnableConversationAction) + : EnableConversationAction() +{ + SplitConversationIdFromCaseFile(pStagingEnableConversationAction->ConversationId, &encounterId, &conversationId); +} + +EditorDialogContents * Conversation::EnableConversationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EnableConversationActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::EnableConversationAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::EnableConversationAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pAction, isForEdit); + + pAction->encounterId = encounterId; + pAction->conversationId = conversationId; +} + +QList *> Conversation::EnableConversationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EnableConversationActionListItem(this, indentLevel)); + return editorList; +} + +QString EnableConversationActionListItem::GetDisplayString() +{ + QString displayString = "In encounter "; + displayString += UnderlineString(pAction->encounterId); + displayString += ", enable the conversation "; + displayString += UnderlineString(pAction->conversationId); + displayString += "."; + + return displayString; +} + +EnableConversationActionEditorDialogContents::EnableConversationActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::EnableConversationAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEnableConversationAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEnableConversationAction = new Conversation::EnableConversationAction(); + } + + pObject = pEnableConversationAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("In encounter ")); + + EncounterSelector *pEncounterSelector = new EncounterSelector(); + pEncounterSelector->Reset(); + pLayout->addWidget(pEncounterSelector); + + if (pActionToEdit != NULL) + { + pEncounterSelector->SetToId(pActionToEdit->encounterId); + } + + Encounter *pEncounter = CaseContent::GetInstance()->GetById(pEncounterSelector->GetId()); + + pLayout->addWidget(new QLabel(" enable the conversation ")); + + pConversationSelector = new ConversationSelector(); + pConversationSelector->Reset(pEncounter); + pLayout->addWidget(pConversationSelector); + + if (pActionToEdit != NULL) + { + pConversationSelector->SetToId(pActionToEdit->conversationId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pEnableConversationAction->encounterId = pEncounterSelector->GetId(); + pEnableConversationAction->conversationId = pConversationSelector->GetId(); + } + + QObject::connect(pEncounterSelector, SIGNAL(EncounterSelected(QString)), this, SLOT(EncounterSelectorEncounterSelected(QString))); + QObject::connect(pConversationSelector, SIGNAL(ConversationSelected(QString)), this, SLOT(ConversationSelectorConverationSelected(QString))); +} + +void EnableConversationActionEditorDialogContents::EncounterSelectorEncounterSelected(const QString &encounterId) +{ + pEnableConversationAction->encounterId = encounterId; + + Encounter *pEncounter = CaseContent::GetInstance()->GetById(encounterId); + + pConversationSelector->Reset(pEncounter); + ConversationSelectorConverationSelected(pConversationSelector->GetId()); +} + +void EnableConversationActionEditorDialogContents::ConversationSelectorConverationSelected(const QString &conversationId) +{ + pEnableConversationAction->conversationId = conversationId; +} + +bool EnableConversationActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::EnableEvidenceAction::EnableEvidenceAction(Staging::Conversation::EnableEvidenceAction *pStagingEnableEvidenceAction) +{ + newEvidenceId = pStagingEnableEvidenceAction->EvidenceId; +} + +EditorDialogContents * Conversation::EnableEvidenceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EnableEvidenceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::EnableEvidenceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::EnableEvidenceAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pAction, isForEdit); + + pAction->newEvidenceId = newEvidenceId; +} + +QList *> Conversation::EnableEvidenceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EnableEvidenceActionListItem(this, indentLevel)); + return editorList; +} + +QString EnableEvidenceActionListItem::GetDisplayString() +{ + QString displayString = "Enable evidence "; + displayString += UnderlineString(pAction->newEvidenceId); + displayString += "."; + + return displayString; +} + +EnableEvidenceActionEditorDialogContents::EnableEvidenceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::EnableEvidenceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEnableEvidenceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEnableEvidenceAction = new Conversation::EnableEvidenceAction(); + } + + pObject = pEnableEvidenceAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Enable evidence ")); + + pEvidenceSelector = new EvidenceSelector(); + pLayout->addWidget(pEvidenceSelector); + + if (pActionToEdit != NULL) + { + pEvidenceSelector->SetToId(pActionToEdit->newEvidenceId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pEnableEvidenceAction->newEvidenceId = pEvidenceSelector->GetId(); + } + + QObject::connect(pEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(EvidenceSelectorEvidenceSelected(QString))); +} + +void EnableEvidenceActionEditorDialogContents::EvidenceSelectorEvidenceSelected(const QString &evidenceId) +{ + pEnableEvidenceAction->newEvidenceId = evidenceId; +} + +bool EnableEvidenceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::UpdateEvidenceAction::UpdateEvidenceAction(Staging::Conversation::UpdateEvidenceAction *pStagingUpdateEvidenceAction) +{ + oldEvidenceId = pStagingUpdateEvidenceAction->EvidenceId; + newEvidenceId = pStagingUpdateEvidenceAction->NewEvidenceId; +} + +EditorDialogContents * Conversation::UpdateEvidenceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new UpdateEvidenceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::UpdateEvidenceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::UpdateEvidenceAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pAction, isForEdit); + + pAction->oldEvidenceId = oldEvidenceId; + pAction->newEvidenceId = newEvidenceId; +} + +QList *> Conversation::UpdateEvidenceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new UpdateEvidenceActionListItem(this, indentLevel)); + return editorList; +} + +QString UpdateEvidenceActionListItem::GetDisplayString() +{ + QString displayString = "Update evidence "; + displayString += UnderlineString(pAction->oldEvidenceId); + displayString += " to "; + displayString += UnderlineString(pAction->newEvidenceId); + displayString += "."; + + return displayString; +} + +UpdateEvidenceActionEditorDialogContents::UpdateEvidenceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::UpdateEvidenceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pUpdateEvidenceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pUpdateEvidenceAction = new Conversation::UpdateEvidenceAction(); + } + + pObject = pUpdateEvidenceAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Update evidence ")); + + pOldEvidenceSelector = new EvidenceSelector(); + pLayout->addWidget(pOldEvidenceSelector); + + if (pActionToEdit != NULL) + { + pOldEvidenceSelector->SetToId(pActionToEdit->oldEvidenceId); + } + + pLayout->addWidget(new QLabel(" to ")); + + pNewEvidenceSelector = new EvidenceSelector(); + pLayout->addWidget(pNewEvidenceSelector); + + if (pActionToEdit != NULL) + { + pNewEvidenceSelector->SetToId(pActionToEdit->newEvidenceId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pUpdateEvidenceAction->oldEvidenceId = pOldEvidenceSelector->GetId(); + pUpdateEvidenceAction->newEvidenceId = pNewEvidenceSelector->GetId(); + } + + QObject::connect(pOldEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(OldEvidenceSelectorEvidenceSelected(QString))); + QObject::connect(pNewEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(NewEvidenceSelectorEvidenceSelected(QString))); +} + +void UpdateEvidenceActionEditorDialogContents::OldEvidenceSelectorEvidenceSelected(const QString &evidenceId) +{ + pUpdateEvidenceAction->newEvidenceId = evidenceId; +} + +void UpdateEvidenceActionEditorDialogContents::NewEvidenceSelectorEvidenceSelected(const QString &evidenceId) +{ + pUpdateEvidenceAction->newEvidenceId = evidenceId; +} + +bool UpdateEvidenceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::DisableEvidenceAction::DisableEvidenceAction(Staging::Conversation::DisableEvidenceAction *pStagingDisableEvidenceAction) +{ + newEvidenceId = pStagingDisableEvidenceAction->EvidenceId; +} + +EditorDialogContents * Conversation::DisableEvidenceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new DisableEvidenceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::DisableEvidenceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::DisableEvidenceAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pAction, isForEdit); + + pAction->newEvidenceId = newEvidenceId; +} + +QList *> Conversation::DisableEvidenceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new DisableEvidenceActionListItem(this, indentLevel)); + return editorList; +} + +QString DisableEvidenceActionListItem::GetDisplayString() +{ + QString displayString = "Disable evidence "; + displayString += UnderlineString(pAction->newEvidenceId); + displayString += "."; + + return displayString; +} + +DisableEvidenceActionEditorDialogContents::DisableEvidenceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::DisableEvidenceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pDisableEvidenceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pDisableEvidenceAction = new Conversation::DisableEvidenceAction(); + } + + pObject = pDisableEvidenceAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Disable evidence ")); + + pEvidenceSelector = new EvidenceSelector(); + pLayout->addWidget(pEvidenceSelector); + + if (pActionToEdit != NULL) + { + pEvidenceSelector->SetToId(pActionToEdit->newEvidenceId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pDisableEvidenceAction->newEvidenceId = pEvidenceSelector->GetId(); + } + + QObject::connect(pEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(EvidenceSelectorEvidenceSelected(QString))); +} + +void DisableEvidenceActionEditorDialogContents::EvidenceSelectorEvidenceSelected(const QString &evidenceId) +{ + pDisableEvidenceAction->newEvidenceId = evidenceId; +} + +bool DisableEvidenceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::EnableCutsceneAction::EnableCutsceneAction(Staging::Conversation::EnableCutsceneAction *pStagingEnableCutsceneAction) +{ + cutsceneId = pStagingEnableCutsceneAction->CutsceneId; +} + +EditorDialogContents * Conversation::EnableCutsceneAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EnableCutsceneActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::EnableCutsceneAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::EnableCutsceneAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pAction, isForEdit); + + pAction->cutsceneId = cutsceneId; +} + +QList *> Conversation::EnableCutsceneAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EnableCutsceneActionListItem(this, indentLevel)); + return editorList; +} + +QString EnableCutsceneActionListItem::GetDisplayString() +{ + QString displayString = "Enable cutscene "; + displayString += UnderlineString(pAction->cutsceneId); + displayString += "."; + + return displayString; +} + +EnableCutsceneActionEditorDialogContents::EnableCutsceneActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::EnableCutsceneAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEnableCutsceneAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEnableCutsceneAction = new Conversation::EnableCutsceneAction(); + } + + pObject = pEnableCutsceneAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Enable cutscene ")); + + /*pEvidenceSelector = new EvidenceSelector(); + pLayout->addWidget(pEvidenceSelector); + + if (pActionToEdit != NULL) + { + pEvidenceSelector->SetToId(pActionToEdit->newEvidenceId); + }*/ + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + /*if (pActionToEdit == NULL) + { + pEnableCutsceneAction->newEvidenceId = pEvidenceSelector->GetId(); + } + + QObject::connect(pEvidenceSelector, SIGNAL(EvidenceSelected(QString)), this, SLOT(EvidenceSelectorEvidenceSelected(QString)));*/ +} + +/*void EnableCutsceneActionEditorDialogContents::EvidenceSelectorEvidenceSelected(const QString &evidenceId) +{ + pEnableCutsceneAction->newEvidenceId = evidenceId; +}*/ + +bool EnableCutsceneActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::PlayBgmAction::PlayBgmAction() + : Action() +{ + preserveBgm = false; +} + +Conversation::PlayBgmAction::PlayBgmAction(Staging::Conversation::PlayBgmAction *pStagingPlayBgmAction) + : PlayBgmAction() +{ + bgmId = pStagingPlayBgmAction->BgmId; + preserveBgm = pStagingPlayBgmAction->PreserveBgm; +} + +EditorDialogContents * Conversation::PlayBgmAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new PlayBgmActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::PlayBgmAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::PlayBgmAction *pAction = static_cast(pOther); + + pAction->bgmId = bgmId; + pAction->preserveBgm = preserveBgm; +} + +QList *> Conversation::PlayBgmAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new PlayBgmActionListItem(this, indentLevel)); + return editorList; +} + +QString PlayBgmActionListItem::GetDisplayString() +{ + QString displayString = "Play the background music "; + displayString += UnderlineString(pAction->bgmId); + + if (pAction->preserveBgm) + { + displayString += " and let it continue after the conversation finishes."; + } + else + { + displayString += " until the conversation finishes."; + } + + return displayString; +} + +PlayBgmActionEditorDialogContents::PlayBgmActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::PlayBgmAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pPlayBgmAction = static_cast(pActionToEdit->Clone()); + } + else + { + pPlayBgmAction = new Conversation::PlayBgmAction(); + } + + pObject = pPlayBgmAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + QHBoxLayout *pBgmSelectionLayout = new QHBoxLayout(); + + pBgmSelectionLayout->addWidget(new QLabel("Play the background music ")); + + BackgroundMusicSelector *pBgmSelector = new BackgroundMusicSelector(); + pBgmSelectionLayout->addWidget(pBgmSelector); + + if (pActionToEdit != NULL) + { + pBgmSelector->SetToId(pActionToEdit->bgmId); + } + + pLayout->addLayout(pBgmSelectionLayout); + + QCheckBox *pPreserveCheckBox = new QCheckBox(); + pPreserveCheckBox->setText("Continue this music after the conversation finishes"); + pLayout->addWidget(pPreserveCheckBox); + + if (pActionToEdit != NULL) + { + pPreserveCheckBox->setChecked(pActionToEdit->preserveBgm); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pPlayBgmAction->bgmId = pBgmSelector->GetId(); + pPlayBgmAction->preserveBgm = pPreserveCheckBox->isChecked(); + } + + QObject::connect(pBgmSelector, SIGNAL(BackgroundMusicSelected(QString)), this, SLOT(BackgroundMusicSelectorBackgroundMusicSelected(QString))); + QObject::connect(pPreserveCheckBox, SIGNAL(toggled(bool)), this, SLOT(PreserveCheckBoxToggled(bool))); +} + +void PlayBgmActionEditorDialogContents::BackgroundMusicSelectorBackgroundMusicSelected(const QString &bgmId) +{ + pPlayBgmAction->bgmId = bgmId; +} + +void PlayBgmActionEditorDialogContents::PreserveCheckBoxToggled(bool isChecked) +{ + pPlayBgmAction->preserveBgm = isChecked; +} + +bool PlayBgmActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::PauseBgmAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new PauseBgmActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::PauseBgmAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::PauseBgmAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new PauseBgmActionListItem(this, indentLevel)); + return editorList; +} + +QString PauseBgmActionListItem::GetDisplayString() +{ + QString displayString = "Pause the currently playing background music."; + + return displayString; +} + +PauseBgmActionEditorDialogContents::PauseBgmActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::PauseBgmAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pPauseBgmAction = static_cast(pActionToEdit->Clone()); + } + else + { + pPauseBgmAction = new Conversation::PauseBgmAction(); + } + + pObject = pPauseBgmAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Pause the currently playing background music.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool PauseBgmActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::ResumeBgmAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ResumeBgmActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::ResumeBgmAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::ResumeBgmAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new ResumeBgmActionListItem(this, indentLevel)); + return editorList; +} + +QString ResumeBgmActionListItem::GetDisplayString() +{ + QString displayString = "Resume the paused background music."; + + return displayString; +} + +ResumeBgmActionEditorDialogContents::ResumeBgmActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::ResumeBgmAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pResumeBgmAction = static_cast(pActionToEdit->Clone()); + } + else + { + pResumeBgmAction = new Conversation::ResumeBgmAction(); + } + + pObject = pResumeBgmAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Resume the paused background music.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool ResumeBgmActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::StopBgmAction::StopBgmAction() + : Action() +{ + isInstant = false; + preserveBgm = false; +} + +Conversation::StopBgmAction::StopBgmAction(Staging::Conversation::StopBgmAction *pStagingStopBgmAction) + : StopBgmAction() +{ + isInstant = pStagingStopBgmAction->IsInstant; + preserveBgm = pStagingStopBgmAction->PreserveBgm; +} + +EditorDialogContents * Conversation::StopBgmAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new StopBgmActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::StopBgmAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::StopBgmAction *pAction = static_cast(pOther); + + pAction->isInstant = isInstant; + pAction->preserveBgm = preserveBgm; +} + +QList *> Conversation::StopBgmAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new StopBgmActionListItem(this, indentLevel)); + return editorList; +} + +QString StopBgmActionListItem::GetDisplayString() +{ + QString displayString = "Stop the currently playing background music "; + + if (pAction->isInstant) + { + displayString += " instantly"; + } + else + { + displayString += " after a fade-out delay"; + } + + displayString += ","; + + if (pAction->preserveBgm) + { + displayString += " and keep it stopped after the conversation finishes."; + } + else + { + displayString += " until the conversation finishes."; + } + + return displayString; +} + +StopBgmActionEditorDialogContents::StopBgmActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::StopBgmAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pStopBgmAction = static_cast(pActionToEdit->Clone()); + } + else + { + pStopBgmAction = new Conversation::StopBgmAction(); + } + + pObject = pStopBgmAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Stop the currently playing background music")); + + QCheckBox *pIsInstantCheckBox = new QCheckBox(); + pIsInstantCheckBox->setText("Stop instantly, rather than allowing it to fade out"); + pLayout->addWidget(pIsInstantCheckBox); + + if (pActionToEdit != NULL) + { + pIsInstantCheckBox->setChecked(pActionToEdit->isInstant); + } + + QCheckBox *pPreserveCheckBox = new QCheckBox(); + pPreserveCheckBox->setText("Keep the music stopped after the conversation finishes"); + pLayout->addWidget(pPreserveCheckBox); + + if (pActionToEdit != NULL) + { + pPreserveCheckBox->setChecked(pActionToEdit->preserveBgm); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pStopBgmAction->isInstant = pIsInstantCheckBox->isChecked(); + pStopBgmAction->preserveBgm = pPreserveCheckBox->isChecked(); + } + + QObject::connect(pIsInstantCheckBox, SIGNAL(toggled(bool)), this, SLOT(IsInstantCheckBoxToggled(bool))); + QObject::connect(pPreserveCheckBox, SIGNAL(toggled(bool)), this, SLOT(PreserveCheckBoxToggled(bool))); +} + +void StopBgmActionEditorDialogContents::IsInstantCheckBoxToggled(bool isChecked) +{ + pStopBgmAction->isInstant = isChecked; +} + +void StopBgmActionEditorDialogContents::PreserveCheckBoxToggled(bool isChecked) +{ + pStopBgmAction->preserveBgm = isChecked; +} + +bool StopBgmActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::PlayAmbianceAction::PlayAmbianceAction() + : Action() +{ + preserveAmbiance = false; +} + +Conversation::PlayAmbianceAction::PlayAmbianceAction(Staging::Conversation::PlayAmbianceAction *pStagingPlayAmbianceAction) +{ + ambianceSfxId = pStagingPlayAmbianceAction->AmbianceSfxId; + preserveAmbiance = pStagingPlayAmbianceAction->PreserveAmbiance; +} + +EditorDialogContents * Conversation::PlayAmbianceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new PlayAmbianceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::PlayAmbianceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::PlayAmbianceAction *pAction = static_cast(pOther); + + pAction->ambianceSfxId = ambianceSfxId; + pAction->preserveAmbiance = preserveAmbiance; +} + +QList *> Conversation::PlayAmbianceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new PlayAmbianceActionListItem(this, indentLevel)); + return editorList; +} + +QString PlayAmbianceActionListItem::GetDisplayString() +{ + QString displayString = "Play the ambiance "; + displayString += UnderlineString(pAction->ambianceSfxId); + + if (pAction->preserveAmbiance) + { + displayString += " and let it continue after the conversation finishes."; + } + else + { + displayString += " until the conversation finishes."; + } + + return displayString; +} + +PlayAmbianceActionEditorDialogContents::PlayAmbianceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::PlayAmbianceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pPlayAmbianceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pPlayAmbianceAction = new Conversation::PlayAmbianceAction(); + } + + pObject = pPlayAmbianceAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + QHBoxLayout *pSfxSelectionLayout = new QHBoxLayout(); + + pSfxSelectionLayout->addWidget(new QLabel("Play the ambiance ")); + + SoundEffectSelector *pSfxSelector = new SoundEffectSelector(); + pSfxSelectionLayout->addWidget(pSfxSelector); + + if (pActionToEdit != NULL) + { + pSfxSelector->SetToId(pActionToEdit->ambianceSfxId); + } + + pLayout->addLayout(pSfxSelectionLayout); + + QCheckBox *pPreserveCheckBox = new QCheckBox(); + pPreserveCheckBox->setText("Continue this ambiance after the conversation finishes"); + pLayout->addWidget(pPreserveCheckBox); + + if (pActionToEdit != NULL) + { + pPreserveCheckBox->setChecked(pActionToEdit->preserveAmbiance); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pPlayAmbianceAction->ambianceSfxId = pSfxSelector->GetId(); + pPlayAmbianceAction->preserveAmbiance = pPreserveCheckBox->isChecked(); + } + + QObject::connect(pSfxSelector, SIGNAL(SoundEffectSelected(QString)), this, SLOT(SoundEffectSelectorSoundEffectSelected(QString))); + QObject::connect(pPreserveCheckBox, SIGNAL(toggled(bool)), this, SLOT(PreserveCheckBoxToggled(bool))); +} + +void PlayAmbianceActionEditorDialogContents::SoundEffectSelectorSoundEffectSelected(const QString &sfxId) +{ + pPlayAmbianceAction->ambianceSfxId = sfxId; +} + +void PlayAmbianceActionEditorDialogContents::PreserveCheckBoxToggled(bool isChecked) +{ + pPlayAmbianceAction->preserveAmbiance = isChecked; +} + +bool PlayAmbianceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::PauseAmbianceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new PauseAmbianceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::PauseAmbianceAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::PauseAmbianceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new PauseAmbianceActionListItem(this, indentLevel)); + return editorList; +} + +QString PauseAmbianceActionListItem::GetDisplayString() +{ + QString displayString = "Pause the currently playing ambiance."; + + return displayString; +} + +PauseAmbianceActionEditorDialogContents::PauseAmbianceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::PauseAmbianceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pPauseAmbianceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pPauseAmbianceAction = new Conversation::PauseAmbianceAction(); + } + + pObject = pPauseAmbianceAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Pause the currently playing ambiance.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool PauseAmbianceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::ResumeAmbianceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ResumeAmbianceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::ResumeAmbianceAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::ResumeAmbianceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new ResumeAmbianceActionListItem(this, indentLevel)); + return editorList; +} + +QString ResumeAmbianceActionListItem::GetDisplayString() +{ + QString displayString = "Resume the paused ambiance."; + + return displayString; +} + +ResumeAmbianceActionEditorDialogContents::ResumeAmbianceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::ResumeAmbianceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pResumeAmbianceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pResumeAmbianceAction = new Conversation::ResumeAmbianceAction(); + } + + pObject = pResumeAmbianceAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Resume the paused ambiance.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool ResumeAmbianceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::StopAmbianceAction::StopAmbianceAction() + : Action() +{ + isInstant = false; + preserveAmbiance = false; +} + +Conversation::StopAmbianceAction::StopAmbianceAction(Staging::Conversation::StopAmbianceAction *pStagingStopAmbianceAction) + : StopAmbianceAction() +{ + isInstant = pStagingStopAmbianceAction->IsInstant; + preserveAmbiance = pStagingStopAmbianceAction->PreserveAmbiance; +} + +EditorDialogContents * Conversation::StopAmbianceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new StopAmbianceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::StopAmbianceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::StopAmbianceAction *pAction = static_cast(pOther); + + pAction->isInstant = isInstant; + pAction->preserveAmbiance = preserveAmbiance; +} + +QList *> Conversation::StopAmbianceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new StopAmbianceActionListItem(this, indentLevel)); + return editorList; +} + +QString StopAmbianceActionListItem::GetDisplayString() +{ + QString displayString = "Stop the currently playing ambiance "; + + if (pAction->isInstant) + { + displayString += " instantly"; + } + else + { + displayString += " after a fade-out delay"; + } + + displayString += ","; + + if (pAction->preserveAmbiance) + { + displayString += " and keep it stopped after the conversation finishes."; + } + else + { + displayString += " until the conversation finishes."; + } + + return displayString; +} + +StopAmbianceActionEditorDialogContents::StopAmbianceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::StopAmbianceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pStopAmbianceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pStopAmbianceAction = new Conversation::StopAmbianceAction(); + } + + pObject = pStopAmbianceAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Stop the currently playing ambiance")); + + QCheckBox *pIsInstantCheckBox = new QCheckBox(); + pIsInstantCheckBox->setText("Stop instantly, rather than allowing it to fade out"); + pLayout->addWidget(pIsInstantCheckBox); + + if (pActionToEdit != NULL) + { + pIsInstantCheckBox->setChecked(pActionToEdit->isInstant); + } + + QCheckBox *pPreserveCheckBox = new QCheckBox(); + pPreserveCheckBox->setText("Keep the ambiance stopped after the conversation finishes"); + pLayout->addWidget(pPreserveCheckBox); + + if (pActionToEdit != NULL) + { + pPreserveCheckBox->setChecked(pActionToEdit->preserveAmbiance); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pStopAmbianceAction->isInstant = pIsInstantCheckBox->isChecked(); + pStopAmbianceAction->preserveAmbiance = pPreserveCheckBox->isChecked(); + } + + QObject::connect(pIsInstantCheckBox, SIGNAL(toggled(bool)), this, SLOT(IsInstantCheckBoxToggled(bool))); + QObject::connect(pPreserveCheckBox, SIGNAL(toggled(bool)), this, SLOT(PreserveCheckBoxToggled(bool))); +} + +void StopAmbianceActionEditorDialogContents::IsInstantCheckBoxToggled(bool isChecked) +{ + pStopAmbianceAction->isInstant = isChecked; +} + +void StopAmbianceActionEditorDialogContents::PreserveCheckBoxToggled(bool isChecked) +{ + pStopAmbianceAction->preserveAmbiance = isChecked; +} + +bool StopAmbianceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::StartAnimationAction::StartAnimationAction(Staging::Conversation::StartAnimationAction *pStartAnimationAction) + : StartAnimationAction() +{ + animationId = pStartAnimationAction->AnimationId; +} + +EditorDialogContents * Conversation::StartAnimationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new StartAnimationActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::StartAnimationAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::StartAnimationAction *pAction = static_cast(pOther); + + pAction->animationId = animationId; +} + +QList *> Conversation::StartAnimationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new StartAnimationActionListItem(this, indentLevel)); + return editorList; +} + +QString StartAnimationActionListItem::GetDisplayString() +{ + QString displayString = "Start the animation " + UnderlineString(pAction->animationId) + "."; + + return displayString; +} + +StartAnimationActionEditorDialogContents::StartAnimationActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::StartAnimationAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pStartAnimationAction = static_cast(pActionToEdit->Clone()); + } + else + { + pStartAnimationAction = new Conversation::StartAnimationAction(); + } + + pObject = pStartAnimationAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Start the animation ")); + + if (pActionToEdit != NULL) + { + pLayout->addWidget(new QLabel(pActionToEdit->animationId)); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool StartAnimationActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::StopAnimationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new StopAnimationActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::StopAnimationAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::StopAnimationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new StopAnimationActionListItem(this, indentLevel)); + return editorList; +} + +QString StopAnimationActionListItem::GetDisplayString() +{ + QString displayString = "Stop the currently running animation."; + + return displayString; +} + +StopAnimationActionEditorDialogContents::StopAnimationActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::StopAnimationAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pStopAnimationAction = static_cast(pActionToEdit->Clone()); + } + else + { + pStopAnimationAction = new Conversation::StopAnimationAction(); + } + + pObject = pStopAnimationAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Stop the currently running animation.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool StopAnimationActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::SetPartnerAction::SetPartnerAction(Staging::Conversation::SetPartnerAction *pSetPartnerAction) + : SetPartnerAction() +{ + partnerId = pSetPartnerAction->PartnerId; +} + +EditorDialogContents * Conversation::SetPartnerAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new SetPartnerActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::SetPartnerAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Conversation::SetPartnerAction *pAction = static_cast(pOther); + + Conversation::ActionWithNotification::CopyPropertiesImpl(pAction, isForEdit); + pAction->partnerId = partnerId; +} + +QList *> Conversation::SetPartnerAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new SetPartnerActionListItem(this, indentLevel)); + return editorList; +} + +QString SetPartnerActionListItem::GetDisplayString() +{ + QString displayString = "Set the player's partner to " + UnderlineString(pAction->partnerId) + "."; + + return displayString; +} + +SetPartnerActionEditorDialogContents::SetPartnerActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::SetPartnerAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pSetPartnerAction = static_cast(pActionToEdit->Clone()); + } + else + { + pSetPartnerAction = new Conversation::SetPartnerAction(); + } + + pObject = pSetPartnerAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Set the player's partner to ")); + + CharacterSelector *pPartnerSelector = new CharacterSelector(false /* allowNoCharacter */); + pLayout->addWidget(pPartnerSelector); + + if (pActionToEdit != NULL) + { + pPartnerSelector->SetToCharacterById(pActionToEdit->partnerId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pSetPartnerAction->partnerId = pPartnerSelector->GetSelectedCharacterId(); + } + + QObject::connect(pPartnerSelector, SIGNAL(CharacterSelected(QString)), this, SLOT(PartnerSelectorCharacterSelected(QString))); +} + +void SetPartnerActionEditorDialogContents::PartnerSelectorCharacterSelected(const QString &partnerId) +{ + pSetPartnerAction->partnerId = partnerId; +} + +bool SetPartnerActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::GoToPresentWrongEvidenceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new GoToPresentWrongEvidenceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::GoToPresentWrongEvidenceAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::GoToPresentWrongEvidenceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new GoToPresentWrongEvidenceActionListItem(this, indentLevel)); + return editorList; +} + +QString GoToPresentWrongEvidenceActionListItem::GetDisplayString() +{ + QString displayString = "Go to the \"presented wrong evidence\" dialog section."; + + return displayString; +} + +GoToPresentWrongEvidenceActionEditorDialogContents::GoToPresentWrongEvidenceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::GoToPresentWrongEvidenceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pGoToPresentWrongEvidenceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pGoToPresentWrongEvidenceAction = new Conversation::GoToPresentWrongEvidenceAction(); + } + + pObject = pGoToPresentWrongEvidenceAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Go to the \"presented wrong evidence\" dialog section.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool GoToPresentWrongEvidenceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::LockConversationAction::LockConversationAction() +{ + pUnlockCondition = NULL; +} + +Conversation::LockConversationAction::LockConversationAction(Staging::Conversation::LockConversationAction *pLockConversationAction) + : LockConversationAction() +{ + pUnlockCondition = UnlockCondition::LoadFromStagingObject(pLockConversationAction->pUnlockCondition); +} + +Conversation::LockConversationAction::~LockConversationAction() +{ + delete pUnlockCondition; + pUnlockCondition = NULL; +} + +EditorDialogContents * Conversation::LockConversationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new LockConversationActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::LockConversationAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::LockConversationAction *pAction = static_cast(pOther); + + delete pAction->pUnlockCondition; + pAction->pUnlockCondition = pUnlockCondition->Clone(); +} + +QList *> Conversation::LockConversationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new LockConversationActionListItem(this, indentLevel)); + return editorList; +} + +QString LockConversationActionListItem::GetDisplayString() +{ + return pAction->pUnlockCondition->GetDisplayString(); +} + +LockConversationActionEditorDialogContents::LockConversationActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::LockConversationAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pLockConversationAction = static_cast(pActionToEdit->Clone()); + } + else + { + pLockConversationAction = new Conversation::LockConversationAction(); + } + + pObject = pLockConversationAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Lock this conversation until ")); + + QComboBox *pTypeSelectorComboBox = new QComboBox(); + pTypeSelectorComboBox->addItem("the following flag is set..."); + pTypeSelectorComboBox->addItem("the following partner is present..."); + pLayout->addWidget(pTypeSelectorComboBox); + + pFlagEditor = new FlagEditor(); + pLayout->addWidget(pFlagEditor); + + pPartnerSelector = new CharacterSelector(false /* allowNoCharacter */); + pLayout->addWidget(pPartnerSelector); + pPartnerSelector->hide(); + + if (pActionToEdit != NULL) + { + pTypeSelectorComboBox->setCurrentIndex((int)pActionToEdit->pUnlockCondition->GetType()); + + switch (pActionToEdit->pUnlockCondition->GetType()) + { + case Conversation::UnlockCondition::Type::FlagSet: + { + Conversation::FlagSetUnlockCondition *pFlagSetUnlockCondition = static_cast(pActionToEdit->pUnlockCondition); + pFlagEditor->SetToId(pFlagSetUnlockCondition->flagId); + pFlagEditor->show(); + pPartnerSelector->hide(); + } + break; + + case Conversation::UnlockCondition::Type::PartnerPresent: + { + Conversation::PartnerPresentUnlockCondition *pPartnerPresentUnlockCondition = static_cast(pActionToEdit->pUnlockCondition); + pPartnerSelector->SetToCharacterById(pPartnerPresentUnlockCondition->partnerId); + pFlagEditor->hide(); + pPartnerSelector->show(); + } + break; + + default: + throw new MLIException("Invalid unlock condition type."); + } + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + switch ((Conversation::UnlockCondition::Type)pTypeSelectorComboBox->currentIndex()) + { + case Conversation::UnlockCondition::Type::FlagSet: + { + delete pLockConversationAction->pUnlockCondition; + pLockConversationAction->pUnlockCondition = new Conversation::FlagSetUnlockCondition(pFlagEditor->GetId()); + } + break; + + case Conversation::UnlockCondition::Type::PartnerPresent: + { + delete pLockConversationAction->pUnlockCondition; + pLockConversationAction->pUnlockCondition = new Conversation::PartnerPresentUnlockCondition(pPartnerSelector->GetSelectedCharacterId()); + } + break; + + default: + throw new MLIException("Invalid unlock condition type."); + } + } + + QObject::connect(pTypeSelectorComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(TypeSelectorComboBoxCurrentItemChanged(int))); + QObject::connect(pFlagEditor, SIGNAL(FlagSelected(QString)), this, SLOT(FlagEditorFlagSelected(QString))); + QObject::connect(pPartnerSelector, SIGNAL(CharacterSelected(QString)), this, SLOT(PartnerSelectorCharacterSelected(QString))); +} + +void LockConversationActionEditorDialogContents::TypeSelectorComboBoxCurrentItemChanged(int selectedIndex) +{ + switch ((Conversation::UnlockCondition::Type)selectedIndex) + { + case Conversation::UnlockCondition::Type::FlagSet: + { + delete pLockConversationAction->pUnlockCondition; + pLockConversationAction->pUnlockCondition = new Conversation::FlagSetUnlockCondition(pFlagEditor->GetId()); + pFlagEditor->show(); + pPartnerSelector->hide(); + } + break; + + case Conversation::UnlockCondition::Type::PartnerPresent: + { + delete pLockConversationAction->pUnlockCondition; + pLockConversationAction->pUnlockCondition = new Conversation::PartnerPresentUnlockCondition(pPartnerSelector->GetSelectedCharacterId()); + pFlagEditor->hide(); + pPartnerSelector->show(); + } + break; + + default: + throw new MLIException("Invalid unlock condition type."); + } +} + +void LockConversationActionEditorDialogContents::FlagEditorFlagSelected(const QString &flagId) +{ + if (pLockConversationAction->pUnlockCondition->GetType() != Conversation::UnlockCondition::Type::FlagSet) + { + throw new MLIException("The flag editor should only be visible when we're selecting which flag needs to be set."); + } + + Conversation::FlagSetUnlockCondition *pFlagSetUnlockCondition = static_cast(pLockConversationAction->pUnlockCondition); + pFlagSetUnlockCondition->flagId = flagId; +} + +void LockConversationActionEditorDialogContents::PartnerSelectorCharacterSelected(const QString &partnerId) +{ + if (pLockConversationAction->pUnlockCondition->GetType() != Conversation::UnlockCondition::Type::PartnerPresent) + { + throw new MLIException("The partner selector should only be visible when we're selecting which partner needs to be present."); + } + + Conversation::PartnerPresentUnlockCondition *pPartnerPresentUnlockCondition = static_cast(pLockConversationAction->pUnlockCondition); + pPartnerPresentUnlockCondition->partnerId = partnerId; +} + +bool LockConversationActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::ExitEncounterAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ExitEncounterActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::ExitEncounterAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::ExitEncounterAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new ExitEncounterActionListItem(this, indentLevel)); + return editorList; +} + +QString ExitEncounterActionListItem::GetDisplayString() +{ + QString displayString = "Exit the current encounter."; + + return displayString; +} + +ExitEncounterActionEditorDialogContents::ExitEncounterActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::ExitEncounterAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pExitEncounterAction = static_cast(pActionToEdit->Clone()); + } + else + { + pExitEncounterAction = new Conversation::ExitEncounterAction(); + } + + pObject = pExitEncounterAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Exit the current encounter.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool ExitEncounterActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::MoveToLocationAction::MoveToLocationAction(Staging::Conversation::MoveToLocationAction *pMoveToLocationAction) + : MoveToLocationAction() +{ + newAreaId = pMoveToLocationAction->NewAreaId; + newLocationId = pMoveToLocationAction->NewLocationId; + transitionId = pMoveToLocationAction->TransitionId; +} + +EditorDialogContents * Conversation::MoveToLocationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new MoveToLocationActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::MoveToLocationAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::MoveToLocationAction *pAction = static_cast(pOther); + + pAction->newAreaId = newAreaId; + pAction->newLocationId = newLocationId; + pAction->transitionId = transitionId; +} + +QList *> Conversation::MoveToLocationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new MoveToLocationActionListItem(this, indentLevel)); + return editorList; +} + +QString MoveToLocationActionListItem::GetDisplayString() +{ + QString displayString = QString("Move the player to location ") + UnderlineString(pAction->newLocationId) + QString(", at transition ") + UnderlineString(pAction->transitionId) + "."; + + return displayString; +} + +MoveToLocationActionEditorDialogContents::MoveToLocationActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::MoveToLocationAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pMoveToLocationAction = static_cast(pActionToEdit->Clone()); + } + else + { + pMoveToLocationAction = new Conversation::MoveToLocationAction(); + } + + pObject = pMoveToLocationAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Move the player to location ")); + + LocationSelector *pLocationSelector = new LocationSelector(); + pLayout->addWidget(pLocationSelector); + + if (pActionToEdit != NULL) + { + pLocationSelector->SetToId(pActionToEdit->newLocationId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pMoveToLocationAction->newLocationId = pLocationSelector->GetId(); + } + + QObject::connect(pLocationSelector, SIGNAL(LocationSelected(QString)), this, SLOT(LocationSelectorLocationSelected(QString))); +} + +void MoveToLocationActionEditorDialogContents::LocationSelectorLocationSelected(const QString &locationId) +{ + pMoveToLocationAction->newLocationId = locationId; +} + +void MoveToLocationActionEditorDialogContents::TransitionIdComboBoxCurrentItemChanged(const QString &transitionId) +{ + pMoveToLocationAction->transitionId = transitionId; +} + +bool MoveToLocationActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::MoveToZoomedViewAction::MoveToZoomedViewAction(Staging::Conversation::MoveToZoomedViewAction *pMoveToZoomedViewAction) + : MoveToZoomedViewAction() +{ + zoomedViewId = pMoveToZoomedViewAction->ZoomedViewId; +} + +EditorDialogContents * Conversation::MoveToZoomedViewAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new MoveToZoomedViewActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::MoveToZoomedViewAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::MoveToZoomedViewAction *pAction = static_cast(pOther); + + pAction->zoomedViewId = zoomedViewId; +} + +QList *> Conversation::MoveToZoomedViewAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new MoveToZoomedViewActionListItem(this, indentLevel)); + return editorList; +} + +QString MoveToZoomedViewActionListItem::GetDisplayString() +{ + QString displayString = QString("Move the player to zoomed view ") + UnderlineString(pAction->zoomedViewId) + "."; + + return displayString; +} + +MoveToZoomedViewActionEditorDialogContents::MoveToZoomedViewActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::MoveToZoomedViewAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pMoveToZoomedViewAction = static_cast(pActionToEdit->Clone()); + } + else + { + pMoveToZoomedViewAction = new Conversation::MoveToZoomedViewAction(); + } + + pObject = pMoveToZoomedViewAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Move the player to zoomed view ")); + + /*QComboBox *pComboBoxId = new QComboBox(); + pLayout->addWidget(pComboBoxId); + + if (pActionToEdit != NULL) + { + pComboBoxId->SetToId(pActionToEdit->newLocationId); + }*/ + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + /*if (pActionToEdit == NULL) + { + pMoveToLocationAction->newLocationId = pLocationSelector->GetId(); + }*/ + + //QObject::connect(pLocationSelector, SIGNAL(LocationSelected(QString)), this, SLOT(LocationSelectorLocationSelected(QString))); +} + +void MoveToZoomedViewActionEditorDialogContents::ZoomedViewIdComboBoxCurrentItemChanged(const QString &zoomedViewId) +{ + pMoveToZoomedViewAction->zoomedViewId = zoomedViewId; +} + +bool MoveToZoomedViewActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::EndCaseAction::EndCaseAction() +{ + completesCase = false; +} + +Conversation::EndCaseAction::EndCaseAction(Staging::Conversation::EndCaseAction *pEndCaseAction) + : EndCaseAction() +{ + completesCase = pEndCaseAction->CompletesCase; +} + +EditorDialogContents * Conversation::EndCaseAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EndCaseActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::EndCaseAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::EndCaseAction *pAction = static_cast(pOther); + + pAction->completesCase = completesCase; +} + +QList *> Conversation::EndCaseAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EndCaseActionListItem(this, indentLevel)); + return editorList; +} + +QString EndCaseActionListItem::GetDisplayString() +{ + QString displayString = "End the current case"; + + if (pAction->completesCase) + { + displayString += ", and mark it as completed."; + } + else + { + displayString += ", but don't mark it as completed."; + } + + return displayString; +} + +EndCaseActionEditorDialogContents::EndCaseActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::EndCaseAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEndCaseAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEndCaseAction = new Conversation::EndCaseAction(); + } + + pObject = pEndCaseAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("End the current case.")); + + QCheckBox *pCompletesCaseCheckBox = new QCheckBox(); + pCompletesCaseCheckBox->setText("...and mark the case as completed"); + pLayout->addWidget(pCompletesCaseCheckBox); + + if (pActionToEdit != NULL) + { + pCompletesCaseCheckBox->setChecked(pActionToEdit->completesCase); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pEndCaseAction->completesCase = pCompletesCaseCheckBox->isChecked(); + } + + QObject::connect(pCompletesCaseCheckBox, SIGNAL(toggled(bool)), this, SLOT(CompletesCaseCheckBoxToggled(bool))); +} + +void EndCaseActionEditorDialogContents::CompletesCaseCheckBoxToggled(bool isChecked) +{ + pEndCaseAction->completesCase = isChecked; +} + +bool EndCaseActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::MultipleChoiceAction::~MultipleChoiceAction() +{ + for (Option *pOption : options) + { + delete pOption; + } + + options.clear(); +} + +EditorDialogContents * Conversation::MultipleChoiceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new MultipleChoiceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::MultipleChoiceAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::MultipleChoiceAction *pAction = static_cast(pOther); + + for (Conversation::MultipleChoiceAction::Option *pOption : pAction->options) + { + delete pOption; + } + + pAction->options.clear(); + + for (Conversation::MultipleChoiceAction::Option *pOption : options) + { + pAction->options.push_back(pOption->Clone()); + } +} + +void Conversation::MultipleChoiceAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Conversation::MultipleChoiceAction *pAction = static_cast(pOther); + + for (Conversation::MultipleChoiceAction::Option *pOption : options) + { + Conversation::MultipleChoiceAction::Option *pOtherOption = NULL; + + for (Conversation::MultipleChoiceAction::Option *pOptionCandidate : pAction->options) + { + if (pOption->GetText() == pOptionCandidate->GetText()) + { + pOtherOption = pOptionCandidate; + break; + } + } + + if (pOtherOption != NULL) + { + pOption->ExchangeListItemBaseOwnership(pOtherOption); + } + } +} + +void Conversation::MultipleChoiceAction::AddOption(const QString &text, const QList &actions) +{ + options.append(new Option(text, actions)); +} + +QList *> Conversation::MultipleChoiceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new MultipleChoiceActionListItem(this, indentLevel)); + + for (Option *pOption : options) + { + editorList.append(new TextDisplay(QString("If the player selects \"") + pOption->GetText() + QString("\":"), indentLevel + 1)); + editorList.append(GetListItemsForActions(pOption->GetActions(), indentLevel + 2)); + } + + return editorList; +} + +QString MultipleChoiceActionListItem::GetDisplayString() +{ + QString displayString = "

Have the player select from the following multiple choice options after showing the previous dialog line:

"; + displayString += "

"; + + for (Conversation::MultipleChoiceAction::Option *pOption : pAction->options) + { + displayString += QString("- ") + pOption->GetText() + "
"; + } + + displayString += "

"; + return displayString; +} + +MultipleChoiceActionEditorDialogContents::MultipleChoiceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::MultipleChoiceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pMultipleChoiceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pMultipleChoiceAction = new Conversation::MultipleChoiceAction(); + } + + pObject = pMultipleChoiceAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + QLabel *pHeaderLabel = new QLabel("Have the player select from the following multiple choice options after showing the previous dialog line:"); + pHeaderLabel->setWordWrap(true); + pLayout->addWidget(pHeaderLabel); + + MultipleSelectionWidget *pOptionSelector = new MultipleSelectionWidget(); + + if (pActionToEdit != NULL && pActionToEdit->options.size() > 0) + { + QStringList strings; + + for (Conversation::MultipleChoiceAction::Option *pOption : pActionToEdit->options) + { + strings.append(pOption->GetText()); + } + + pOptionSelector->SetSelections(strings); + } + else + { + QStringList stringList; + stringList.append(""); + + pOptionSelector->SetSelections(stringList); + } + + pLayout->addWidget(pOptionSelector); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + QObject::connect(pOptionSelector, SIGNAL(StringChanged(int,QString)), this, SLOT(OptionSelectorStringChanged(int,QString))); + QObject::connect(pOptionSelector, SIGNAL(StringAdded(int,QString)), this, SLOT(OptionSelectorStringAdded(int,QString))); + QObject::connect(pOptionSelector, SIGNAL(StringRemoved(int)), this, SLOT(OptionSelectorStringRemoved(int))); +} + +void MultipleChoiceActionEditorDialogContents::OptionSelectorStringChanged(int index, const QString &string) +{ + pMultipleChoiceAction->options[index]->SetText(string); +} + +void MultipleChoiceActionEditorDialogContents::OptionSelectorStringAdded(int index, const QString &string) +{ + pMultipleChoiceAction->options.insert(index, new Conversation::MultipleChoiceAction::Option(string)); +} + +void MultipleChoiceActionEditorDialogContents::OptionSelectorStringRemoved(int index) +{ + Conversation::MultipleChoiceAction::Option *pRemovedOption = pMultipleChoiceAction->options[index]; + pMultipleChoiceAction->options.removeAt(index); + delete pRemovedOption; +} + +bool MultipleChoiceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::MultipleChoiceAction::Option::Option(const QString &text) +{ + this->text = text; +} + +Conversation::MultipleChoiceAction::Option::Option(const QString &text, const QList &actions) +{ + this->text = text; + this->actions = actions; +} + +Conversation::MultipleChoiceAction::Option::~Option() +{ + for (Action *pAction : actions) + { + delete pAction; + } + + actions.clear(); +} + +Conversation::MultipleChoiceAction::Option * Conversation::MultipleChoiceAction::Option::Clone() +{ + Conversation::MultipleChoiceAction::Option *pClonedOption = new Conversation::MultipleChoiceAction::Option(); + + pClonedOption->text = text; + + for (Action *pAction : actions) + { + pClonedOption->actions.push_back(pAction->Clone()); + } + + return pClonedOption; +} + +void Conversation::MultipleChoiceAction::Option::ReplaceAction(Action *pNewAction, Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, actions); +} + +void Conversation::MultipleChoiceAction::Option::ExchangeListItemBaseOwnership(Option *pOther) +{ + Conversation::Action::ExchangeListItemBaseOwnership(pOther->actions, actions); +} + +EditorDialogContents * Conversation::ExitMultipleChoiceAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ExitMultipleChoiceActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::ExitMultipleChoiceAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::ExitMultipleChoiceAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new ExitMultipleChoiceActionListItem(this, indentLevel)); + return editorList; +} + +QString ExitMultipleChoiceActionListItem::GetDisplayString() +{ + QString displayString = "Exit the current multiple-choice branches."; + + return displayString; +} + +ExitMultipleChoiceActionEditorDialogContents::ExitMultipleChoiceActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::ExitMultipleChoiceAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pExitMultipleChoiceAction = static_cast(pActionToEdit->Clone()); + } + else + { + pExitMultipleChoiceAction = new Conversation::ExitMultipleChoiceAction(); + } + + pObject = pExitMultipleChoiceAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Exit the current multiple-choice branches.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool ExitMultipleChoiceActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::EnableFastForwardAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EnableFastForwardActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::EnableFastForwardAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::EnableFastForwardAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EnableFastForwardActionListItem(this, indentLevel)); + return editorList; +} + +QString EnableFastForwardActionListItem::GetDisplayString() +{ + QString displayString = "Enable the player to fast-forward through this conversation."; + + return displayString; +} + +EnableFastForwardActionEditorDialogContents::EnableFastForwardActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::EnableFastForwardAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEnableFastForwardAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEnableFastForwardAction = new Conversation::EnableFastForwardAction(); + } + + pObject = pEnableFastForwardAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Enable the player to fast-forward through this conversation.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool EnableFastForwardActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::DisableFastForwardAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new DisableFastForwardActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::DisableFastForwardAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::DisableFastForwardAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new DisableFastForwardActionListItem(this, indentLevel)); + return editorList; +} + +QString DisableFastForwardActionListItem::GetDisplayString() +{ + QString displayString = "Disable the player from fast-forwarding through this conversation."; + + return displayString; +} + +DisableFastForwardActionEditorDialogContents::DisableFastForwardActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::DisableFastForwardAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pDisableFastForwardAction = static_cast(pActionToEdit->Clone()); + } + else + { + pDisableFastForwardAction = new Conversation::DisableFastForwardAction(); + } + + pObject = pDisableFastForwardAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Disable the player from fast-forwarding through this conversation.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool DisableFastForwardActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Conversation::BeginBreakdownAction::BeginBreakdownAction() +{ + characterPosition = CharacterPositionNone; +} + +Conversation::BeginBreakdownAction::BeginBreakdownAction(Staging::Conversation::BeginBreakdownAction *pBeginBreakdownAction) + : BeginBreakdownAction() +{ + characterPosition = (CharacterPosition)pBeginBreakdownAction->CharacterPositionValue; +} + +EditorDialogContents * Conversation::BeginBreakdownAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new BeginBreakdownActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::BeginBreakdownAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Conversation::BeginBreakdownAction *pAction = static_cast(pOther); + + pAction->characterPosition = characterPosition; +} + +QList *> Conversation::BeginBreakdownAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new BeginBreakdownActionListItem(this, indentLevel)); + return editorList; +} + +QString BeginBreakdownActionListItem::GetDisplayString() +{ + QString speakerName; + + Character *pLeftCharacter = CaseContent::GetInstance()->GetById(stateBeforeObject.GetLeftCharacterId()); + Character *pRightCharacter = CaseContent::GetInstance()->GetById(stateBeforeObject.GetRightCharacterId()); + + switch (pAction->characterPosition) + { + case CharacterPositionLeft: + speakerName = (pLeftCharacter != NULL ? pLeftCharacter->GetDisplayName() : "no one") + " (the left character)"; + break; + case CharacterPositionRight: + speakerName = (pRightCharacter != NULL ? pRightCharacter->GetDisplayName() : "no one") + " (the right character)"; + break; + default: + throw new MLIException("Invalid character position for breakdown."); + } + + QString displayString = "Begin the breakdown animation for " + UnderlineString(speakerName); + + return displayString; +} + +BeginBreakdownActionEditorDialogContents::BeginBreakdownActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::BeginBreakdownAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pBeginBreakdownAction = static_cast(pActionToEdit->Clone()); + } + else + { + pBeginBreakdownAction = new Conversation::BeginBreakdownAction(); + } + + pObject = pBeginBreakdownAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Begin the breakdown animation for ")); + + QComboBox *pCharacterComboBox = new QComboBox(); + + Character *pLeftCharacter = CaseContent::GetInstance()->GetById(this->stateBeforeObject.GetLeftCharacterId()); + Character *pRightCharacter = CaseContent::GetInstance()->GetById(this->stateBeforeObject.GetRightCharacterId()); + + pCharacterComboBox->addItem((pLeftCharacter != NULL ? pLeftCharacter->GetDisplayName() : "no one") + " (the left character)"); + pCharacterComboBox->addItem((pRightCharacter != NULL ? pRightCharacter->GetDisplayName() : "no one") + " (the right character)"); + pLayout->addWidget(pCharacterComboBox); + + if (pActionToEdit != NULL) + { + switch (pActionToEdit->characterPosition) + { + case CharacterPositionLeft: + pCharacterComboBox->setCurrentIndex(0); + break; + case CharacterPositionRight: + pCharacterComboBox->setCurrentIndex(1); + break; + default: + throw new MLIException("Invalid character position for breakdown."); + } + } + + QObject::connect(pCharacterComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SpeakerComboBoxCurrentIndexChanged(int))); + + CharacterComboBoxCurrentIndexChanged(pCharacterComboBox->currentIndex()); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +void BeginBreakdownActionEditorDialogContents::CharacterComboBoxCurrentIndexChanged(int newIndex) +{ + switch (newIndex) + { + case 0: + pBeginBreakdownAction->characterPosition = CharacterPositionLeft; + break; + case 1: + pBeginBreakdownAction->characterPosition = CharacterPositionRight; + break; + default: + throw new MLIException("Unexpected SpeakerComboBox index selected."); + } +} + +bool BeginBreakdownActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Conversation::EndBreakdownAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EndBreakdownActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Conversation::EndBreakdownAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Conversation::EndBreakdownAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EndBreakdownActionListItem(this, indentLevel)); + return editorList; +} + +QString EndBreakdownActionListItem::GetDisplayString() +{ + QString displayString = "End the currently running breakdown animation."; + + return displayString; +} + +EndBreakdownActionEditorDialogContents::EndBreakdownActionEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::EndBreakdownAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEndBreakdownAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEndBreakdownAction = new Conversation::EndBreakdownAction(); + } + + pObject = pEndBreakdownAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("End the currently running breakdown animation.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool EndBreakdownActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Interrogation::Interrogation(Staging::Interrogation *pStagingInterrogation) : + Conversation(pStagingInterrogation) +{ + +} + +void Interrogation::WriteToCaseXml(XmlWriter *pWriter) +{ + pWriter->StartElement("Interrogation"); + WriteToCaseXmlCore(pWriter); + pWriter->EndElement(); +} + +void Interrogation::PopulateActionFromStaging(QList &actions, const QList &stagingActions, int &indexInStagingActions, int initialIndexInFullList) +{ + Staging::Conversation::Action *pAction = stagingActions[indexInStagingActions]; + + switch (pAction->GetType()) + { + case Staging::ConversationActionType_BeginInterrogationRepeat: + { + Staging::Interrogation::BeginInterrogationRepeatAction *pStagingBeginInterrogationRepeatAction = + static_cast(pAction); + + InterrogationRepeatAction *pInterrogationRepeatAction = new InterrogationRepeatAction(pStagingBeginInterrogationRepeatAction); + + int endIndex = -1; + + for (int j = indexInStagingActions + 1; j < stagingActions.count(); j++) + { + if (stagingActions[j]->GetType() == Staging::ConversationActionType_EndInterrogationRepeat) + { + endIndex = j; + break; + } + else if (stagingActions[j]->GetType() == Staging::ConversationActionType_BeginInterrogationRepeat) + { + throw new MLIException("We should never have nested InterrogationRepeatActions."); + } + } + + if (endIndex < 0) + { + throw new MLIException("Expected an EndInterrogationRepeatAction to follow a BeginInterrogationRepeatAction."); + } + + int wrongEvidencePresentedIndex = -1; + int wrongPartnerPresentedIndex = -1; + int endRequestedIndex = -1; + + QMap > partialStagingActionsToRepeatByStartIndex; + QList currentPartialStagingActions; + int currentPartialStagingActionsStartIndex = -1; + + QList wrongEvidencePresentedStagingActions; + QList wrongPartnerUsedStagingActions; + QList endRequestedStagingActions; + + for (int j = indexInStagingActions + 1; j < endIndex; j++) + { + Staging::Conversation::Action *pInnerStagingAction = stagingActions[j]; + + if (pInnerStagingAction->GetType() == Staging::ConversationActionType_BeginWrongEvidencePresented) + { + if (currentPartialStagingActionsStartIndex != -1) + { + partialStagingActionsToRepeatByStartIndex.insert(currentPartialStagingActionsStartIndex, currentPartialStagingActions); + currentPartialStagingActions.clear(); + currentPartialStagingActionsStartIndex = -1; + } + + wrongEvidencePresentedIndex = j; + int k = -1; + + for (k = j; k < endIndex && stagingActions[k]->GetType() != Staging::ConversationActionType_EndWrongEvidencePresented; k++) + { + wrongEvidencePresentedStagingActions.append(stagingActions[k]); + } + + wrongEvidencePresentedStagingActions.append(stagingActions[k]); + j = k; + } + else if (pInnerStagingAction->GetType() == Staging::ConversationActionType_BeginWrongPartnerUsed) + { + if (currentPartialStagingActionsStartIndex != -1) + { + partialStagingActionsToRepeatByStartIndex.insert(currentPartialStagingActionsStartIndex, currentPartialStagingActions); + currentPartialStagingActions.clear(); + currentPartialStagingActionsStartIndex = -1; + } + + wrongPartnerPresentedIndex = j; + int k = -1; + + for (k = j; k < endIndex && stagingActions[k]->GetType() != Staging::ConversationActionType_EndWrongPartnerUsed; k++) + { + wrongPartnerUsedStagingActions.append(stagingActions[k]); + } + + wrongPartnerUsedStagingActions.append(stagingActions[k]); + j = k; + } + else if (pInnerStagingAction->GetType() == Staging::ConversationActionType_BeginEndRequested) + { + if (currentPartialStagingActionsStartIndex != -1) + { + partialStagingActionsToRepeatByStartIndex.insert(currentPartialStagingActionsStartIndex, currentPartialStagingActions); + currentPartialStagingActions.clear(); + currentPartialStagingActionsStartIndex = -1; + } + + endRequestedIndex = j; + int k = -1; + + for (k = j; k < endIndex && stagingActions[k]->GetType() != Staging::ConversationActionType_EndEndRequested; k++) + { + endRequestedStagingActions.append(stagingActions[k]); + } + + endRequestedStagingActions.append(stagingActions[k]); + j = k; + } + else + { + if (currentPartialStagingActionsStartIndex == -1) + { + currentPartialStagingActionsStartIndex = j; + } + + currentPartialStagingActions.append(pInnerStagingAction); + } + } + + if (currentPartialStagingActionsStartIndex != -1) + { + partialStagingActionsToRepeatByStartIndex.insert(currentPartialStagingActionsStartIndex, currentPartialStagingActions); + currentPartialStagingActions.clear(); + currentPartialStagingActionsStartIndex = -1; + } + + for (int startIndex : partialStagingActionsToRepeatByStartIndex.keys()) + { + QList partialActionsToRepeat; + PopulateActionsFromStaging(partialActionsToRepeat, partialStagingActionsToRepeatByStartIndex[startIndex], startIndex + initialIndexInFullList); + + pInterrogationRepeatAction->actionsToRepeat.append(partialActionsToRepeat); + } + + PopulateActionsFromStaging(pInterrogationRepeatAction->wrongEvidencePresentedActions, wrongEvidencePresentedStagingActions, wrongEvidencePresentedIndex + initialIndexInFullList); + PopulateActionsFromStaging(pInterrogationRepeatAction->wrongPartnerUsedActions, wrongPartnerUsedStagingActions, wrongPartnerPresentedIndex + initialIndexInFullList); + PopulateActionsFromStaging(pInterrogationRepeatAction->endRequestedActions, endRequestedStagingActions, endRequestedIndex + initialIndexInFullList); + + actions.append(pInterrogationRepeatAction); + indexInStagingActions = endIndex - 1; + } + break; + + case Staging::ConversationActionType_ShowInterrogation: + { + Staging::Interrogation::ShowInterrogationAction *pStagingShowInterrogationAction = + static_cast(pAction); + + ShowInterrogationAction *pShowInterrogationAction = new ShowInterrogationAction(pStagingShowInterrogationAction); + + QList pressForInfoStagingActions; + + for (int j = pStagingShowInterrogationAction->PressForInfoIndex - initialIndexInFullList; j < stagingActions.count(); j++) + { + pressForInfoStagingActions.append(stagingActions[j]); + + if (stagingActions[j]->GetType() == Staging::ConversationActionType_EndPressForInfo) + { + break; + } + } + + PopulateActionsFromStaging(pShowInterrogationAction->pressForInfoActions, pressForInfoStagingActions, pStagingShowInterrogationAction->PressForInfoIndex - initialIndexInFullList); + + for (QString evidenceId : pStagingShowInterrogationAction->EvidencePresentedIndexes.keys()) + { + QList evidencePresentedStagingActions; + QList evidencePresentedActions; + + for (int j = pStagingShowInterrogationAction->EvidencePresentedIndexes[evidenceId] - initialIndexInFullList; j < stagingActions.count(); j++) + { + evidencePresentedStagingActions.append(stagingActions[j]); + + if (stagingActions[j]->GetType() == Staging::ConversationActionType_EndPresentEvidence) + { + break; + } + } + + PopulateActionsFromStaging(evidencePresentedActions, evidencePresentedStagingActions, pStagingShowInterrogationAction->EvidencePresentedIndexes[evidenceId] - initialIndexInFullList); + pShowInterrogationAction->evidencePresentedActionsByEvidenceId.insert(evidenceId, evidencePresentedActions); + } + + for (QString partnerId : pStagingShowInterrogationAction->PartnerUsedIndexes.keys()) + { + QList partnerUsedStagingActions; + QList partnerUsedActions; + + for (int j = pStagingShowInterrogationAction->PartnerUsedIndexes[partnerId] - initialIndexInFullList; j < stagingActions.count(); j++) + { + partnerUsedStagingActions.append(stagingActions[j]); + + if (stagingActions[j]->GetType() == Staging::ConversationActionType_EndUsePartner) + { + break; + } + } + + PopulateActionsFromStaging(partnerUsedActions, partnerUsedStagingActions, pStagingShowInterrogationAction->PartnerUsedIndexes[partnerId] - initialIndexInFullList); + pShowInterrogationAction->partnerUsedActionsByPartnerId.insert(partnerId, partnerUsedActions); + } + + actions.append(pShowInterrogationAction); + + indexInStagingActions = (pStagingShowInterrogationAction->NextInterrogationIndex >= 0 ? + pStagingShowInterrogationAction->NextInterrogationIndex : + pStagingShowInterrogationAction->InterrogationFinishIndex) - initialIndexInFullList - 1; + } + break; + + case Staging::ConversationActionType_ExitInterrogationRepeat: + actions.append(new ExitInterrogationRepeatAction(static_cast(pAction))); + break; + + default: + Conversation::PopulateActionFromStaging(actions, stagingActions, indexInStagingActions, initialIndexInFullList); + } +} + +Interrogation::InterrogationRepeatAction::~InterrogationRepeatAction() +{ + for (Action *pAction : actionsToRepeat) + { + delete pAction; + } + + actionsToRepeat.clear(); + + for (Action *pAction : wrongEvidencePresentedActions) + { + delete pAction; + } + + wrongEvidencePresentedActions.clear(); + + for (Action *pAction : wrongPartnerUsedActions) + { + delete pAction; + } + + wrongPartnerUsedActions.clear(); + + for (Action *pAction : endRequestedActions) + { + delete pAction; + } + + endRequestedActions.clear(); +} + +EditorDialogContents * Interrogation::InterrogationRepeatAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new InterrogationRepeatActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Interrogation::InterrogationRepeatAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Interrogation::InterrogationRepeatAction *pAction = static_cast(pOther); + + Conversation::Action::CloneActionList(pAction->actionsToRepeat, actionsToRepeat); + Conversation::Action::CloneActionList(pAction->wrongEvidencePresentedActions, wrongEvidencePresentedActions); + Conversation::Action::CloneActionList(pAction->wrongPartnerUsedActions, wrongPartnerUsedActions); + Conversation::Action::CloneActionList(pAction->endRequestedActions, endRequestedActions); +} + +void Interrogation::InterrogationRepeatAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Interrogation::InterrogationRepeatAction *pAction = static_cast(pOther); + + Conversation::Action::ExchangeListItemBaseOwnership(pAction->actionsToRepeat, actionsToRepeat); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->wrongEvidencePresentedActions, wrongEvidencePresentedActions); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->wrongPartnerUsedActions, wrongPartnerUsedActions); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->endRequestedActions, endRequestedActions); +} + +void Interrogation::InterrogationRepeatAction::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, actionsToRepeat); + Conversation::ReplaceAction(pNewAction, pOldAction, wrongEvidencePresentedActions); + Conversation::ReplaceAction(pNewAction, pOldAction, wrongPartnerUsedActions); + Conversation::ReplaceAction(pNewAction, pOldAction, endRequestedActions); +} + +void Interrogation::InterrogationRepeatAction::UpdateAndCacheConversationState(State ¤tState) +{ + Conversation::Action::UpdateAndCacheConversationState(currentState); + + State actionsToRepeatBranchState = currentState.Clone(); + State wrongEvidencePresentedActionsBranchState = currentState.Clone(); + State wrongPartnerUsedActionsBranchState = currentState.Clone(); + State endRequestedActionsBranchState = currentState.Clone(); + + for (Action *pAction : actionsToRepeat) + { + pAction->UpdateAndCacheConversationState(actionsToRepeatBranchState); + } + + for (Action *pAction : wrongEvidencePresentedActions) + { + pAction->UpdateAndCacheConversationState(wrongEvidencePresentedActionsBranchState); + } + + for (Action *pAction : wrongPartnerUsedActions) + { + pAction->UpdateAndCacheConversationState(wrongPartnerUsedActionsBranchState); + } + + for (Action *pAction : endRequestedActions) + { + pAction->UpdateAndCacheConversationState(endRequestedActionsBranchState); + } + + currentState = actionsToRepeatBranchState.Clone(); +} + +void Interrogation::InterrogationRepeatAction::PreloadAudio() +{ + for (Action *pAction : actionsToRepeat) + { + pAction->PreloadAudio(); + } + + for (Action *pAction : wrongEvidencePresentedActions) + { + pAction->PreloadAudio(); + } + + for (Action *pAction : wrongPartnerUsedActions) + { + pAction->PreloadAudio(); + } + + for (Action *pAction : endRequestedActions) + { + pAction->PreloadAudio(); + } +} + +QList *> Interrogation::InterrogationRepeatAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new InterrogationRepeatActionListItem(this, indentLevel)); + editorList.append(new TextDisplay("Loop until done:", indentLevel + 1)); + editorList.append(GetListItemsForActions(actionsToRepeat, indentLevel + 2)); + editorList.append(new TextDisplay("If the player presents the wrong evidence:", indentLevel + 1)); + editorList.append(GetListItemsForActions(wrongEvidencePresentedActions, indentLevel + 2)); + editorList.append(new TextDisplay("If the player uses the wrong partner:", indentLevel + 1)); + editorList.append(GetListItemsForActions(wrongPartnerUsedActions, indentLevel + 2)); + editorList.append(new TextDisplay("If the player gives up:", indentLevel + 1)); + editorList.append(GetListItemsForActions(endRequestedActions, indentLevel + 2)); + + return editorList; +} + +QString InterrogationRepeatActionListItem::GetDisplayString() +{ + QString displayString = "Repeat the following interrogation actions until the player reaches the exit condition or quits."; + + return displayString; +} + +InterrogationRepeatActionEditorDialogContents::InterrogationRepeatActionEditorDialogContents(const Conversation::State &stateBeforeObject, Interrogation::InterrogationRepeatAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pInterrogationRepeatAction = static_cast(pActionToEdit->Clone()); + } + else + { + pInterrogationRepeatAction = new Interrogation::InterrogationRepeatAction(); + } + + pObject = pInterrogationRepeatAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + QLabel *pLabel = new QLabel("Repeat the following interrogation actions until the player reaches the exit condition or quits."); + pLabel->setWordWrap(true); + pLayout->addWidget(pLabel); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool InterrogationRepeatActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Interrogation::ShowInterrogationAction::ShowInterrogationAction(Staging::Interrogation::ShowInterrogationAction *pShowInterrogationAction) + : ShowDialogAction(pShowInterrogationAction) +{ + conditionFlag = pShowInterrogationAction->ConditionFlag; +} + +Interrogation::ShowInterrogationAction::~ShowInterrogationAction() +{ + for (Action *pAction : pressForInfoActions) + { + delete pAction; + } + + pressForInfoActions.clear(); + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + for (Action *pAction : evidencePresentedActionsByEvidenceId[evidenceId]) + { + delete pAction; + } + + evidencePresentedActionsByEvidenceId[evidenceId].clear(); + } + + evidencePresentedActionsByEvidenceId.clear(); + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + for (Action *pAction : partnerUsedActionsByPartnerId[partnerId]) + { + delete pAction; + } + + partnerUsedActionsByPartnerId[partnerId].clear(); + } + + partnerUsedActionsByPartnerId.clear(); +} + +EditorDialogContents * Interrogation::ShowInterrogationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ShowDialogActionEditorDialogContents(stateBeforeObject, ShowDialogActionEditorDialogContents::Type::ShowInterrogation, dynamic_cast(pActionToEdit)); +} + +void Interrogation::ShowInterrogationAction::CopyPropertiesImpl(Conversation::Action *pOther, bool isForEdit) +{ + Interrogation::ShowInterrogationAction *pAction = static_cast(pOther); + + Conversation::ShowDialogAction::CopyPropertiesImpl(pAction, isForEdit); + + Conversation::Action::CloneActionList(pAction->pressForInfoActions, pressForInfoActions); + + pAction->evidencePresentedActionsByEvidenceId.clear(); + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + Conversation::Action::CloneActionList( + pAction->evidencePresentedActionsByEvidenceId[evidenceId], + evidencePresentedActionsByEvidenceId[evidenceId]); + } + + pAction->partnerUsedActionsByPartnerId.clear(); + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + Conversation::Action::CloneActionList( + pAction->partnerUsedActionsByPartnerId[partnerId], + partnerUsedActionsByPartnerId[partnerId]); + } +} + +void Interrogation::ShowInterrogationAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Interrogation::ShowInterrogationAction *pAction = static_cast(pOther); + + Conversation::Action::ExchangeListItemBaseOwnership(pAction->pressForInfoActions, pressForInfoActions); + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + QString otherEvidenceId = evidenceId; + + if (pAction->originalEvidenceIdToNewEvidenceIdMap.contains(evidenceId)) + { + otherEvidenceId = pAction->originalEvidenceIdToNewEvidenceIdMap[evidenceId]; + } + + if (pAction->evidencePresentedActionsByEvidenceId.contains(otherEvidenceId) && + evidencePresentedActionsByEvidenceId[evidenceId].size() == pAction->evidencePresentedActionsByEvidenceId[otherEvidenceId].size()) + { + Conversation::Action::ExchangeListItemBaseOwnership( + pAction->evidencePresentedActionsByEvidenceId[otherEvidenceId], + evidencePresentedActionsByEvidenceId[evidenceId]); + } + } + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + QString otherPartnerId = partnerId; + + if (pAction->originalPartnerIdToNewPartnerIdMap.contains(partnerId)) + { + otherPartnerId = pAction->originalPartnerIdToNewPartnerIdMap[partnerId]; + } + + if (pAction->partnerUsedActionsByPartnerId.contains(otherPartnerId) && + partnerUsedActionsByPartnerId[partnerId].size() == pAction->partnerUsedActionsByPartnerId[otherPartnerId].size()) + { + Conversation::Action::ExchangeListItemBaseOwnership( + pAction->partnerUsedActionsByPartnerId[otherPartnerId], + partnerUsedActionsByPartnerId[partnerId]); + } + } +} + +void Interrogation::ShowInterrogationAction::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, pressForInfoActions); + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + Conversation::ReplaceAction(pNewAction, pOldAction, evidencePresentedActionsByEvidenceId[evidenceId]); + } + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + Conversation::ReplaceAction(pNewAction, pOldAction, partnerUsedActionsByPartnerId[partnerId]); + } +} + +void Interrogation::ShowInterrogationAction::UpdateAndCacheConversationState(State ¤tState) +{ + Conversation::ShowDialogAction::UpdateAndCacheConversationState(currentState); + + State tempBranchState = currentState.Clone(); + + for (Action *pAction : pressForInfoActions) + { + pAction->UpdateAndCacheConversationState(tempBranchState); + } + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + tempBranchState = currentState.Clone(); + + for (Action *pAction : evidencePresentedActionsByEvidenceId[evidenceId]) + { + pAction->UpdateAndCacheConversationState(tempBranchState); + } + } + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + tempBranchState = currentState.Clone(); + + for (Action *pAction : partnerUsedActionsByPartnerId[partnerId]) + { + pAction->UpdateAndCacheConversationState(tempBranchState); + } + } +} + +void Interrogation::ShowInterrogationAction::PreloadAudio() +{ + Conversation::ShowDialogAction::PreloadAudio(); + + for (Action *pAction : pressForInfoActions) + { + pAction->PreloadAudio(); + } + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + for (Action *pAction : evidencePresentedActionsByEvidenceId[evidenceId]) + { + pAction->PreloadAudio(); + } + } + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + for (Action *pAction : partnerUsedActionsByPartnerId[partnerId]) + { + pAction->PreloadAudio(); + } + } +} + +void Interrogation::ShowInterrogationAction::SaveElementsToXml(XmlWriter *pWriter) +{ + ShowDialogAction::SaveElementsToXml(pWriter); + + if (evidencePresentedActionsByEvidenceId.keys().count() > 0) + { + pWriter->StartElement("EvidencePresentedActionsByEvidenceId"); + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + pWriter->StartElement("Entry"); + pWriter->WriteTextElement("EvidenceId", evidenceId); + + pWriter->StartElement("Actions"); + + for (Action *pAction : evidencePresentedActionsByEvidenceId[evidenceId]) + { + pWriter->StartElement("Action"); + pAction->SaveToXml(pWriter); + pWriter->EndElement(); + } + + pWriter->EndElement(); + + pWriter->EndElement(); + } + + pWriter->EndElement(); + } + + if (partnerUsedActionsByPartnerId.keys().count() > 0) + { + pWriter->StartElement("PartnerUsedActionsByPartnerId"); + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + pWriter->StartElement("Entry"); + pWriter->WriteTextElement("PartnerId", partnerId); + + pWriter->StartElement("Actions"); + + for (Action *pAction : partnerUsedActionsByPartnerId[partnerId]) + { + pWriter->StartElement("Action"); + pAction->SaveToXml(pWriter); + pWriter->EndElement(); + } + + pWriter->EndElement(); + + pWriter->EndElement(); + } + + pWriter->EndElement(); + } +} + +void Interrogation::ShowInterrogationAction::LoadElementsFromXml(XmlReader *pReader) +{ + ShowDialogAction::LoadElementsFromXml(pReader); + + if (pReader->ElementExists("EvidencePresentedActionsByEvidenceId")) + { + pReader->StartElement("EvidencePresentedActionsByEvidenceId"); + pReader->StartList("Entry"); + + while (pReader->MoveToNextListItem()) + { + QString evidenceId = pReader->ReadTextElement("EvidenceId"); + + pReader->StartElement("Actions"); + pReader->StartList("Action"); + + while (pReader->MoveToNextListItem()) + { + evidencePresentedActionsByEvidenceId[evidenceId].push_back(Action::CreateFromXml(pReader)); + } + + pReader->EndElement(); + } + + pReader->EndElement(); + } + + if (pReader->ElementExists("PartnerUsedActionsByPartnerId")) + { + pReader->StartElement("PartnerUsedActionsByPartnerId"); + pReader->StartList("Entry"); + + while (pReader->MoveToNextListItem()) + { + QString partnerId = pReader->ReadTextElement("PartnerId"); + + pReader->StartElement("Actions"); + pReader->StartList("Action"); + + while (pReader->MoveToNextListItem()) + { + partnerUsedActionsByPartnerId[partnerId].push_back(Action::CreateFromXml(pReader)); + } + + pReader->EndElement(); + } + + pReader->EndElement(); + } +} + +QList *> Interrogation::ShowInterrogationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new ShowInterrogationActionListItem(this, indentLevel)); + editorList.append(new TextDisplay("If the player presses for more information:", indentLevel + 1)); + editorList.append(GetListItemsForActions(pressForInfoActions, indentLevel + 2)); + + for (QString evidenceId : evidencePresentedActionsByEvidenceId.keys()) + { + editorList.append(new TextDisplay(QString("If the player presents \"") + evidenceId + QString("\":"), indentLevel + 1)); + editorList.append(GetListItemsForActions(evidencePresentedActionsByEvidenceId[evidenceId], indentLevel + 2)); + } + + for (QString partnerId : partnerUsedActionsByPartnerId.keys()) + { + editorList.append(new TextDisplay(QString("If the player uses \"") + partnerId + QString("\":"), indentLevel + 1)); + editorList.append(GetListItemsForActions(partnerUsedActionsByPartnerId[partnerId], indentLevel + 2)); + } + + return editorList; +} + +QString ShowInterrogationActionListItem::GetHeaderString(const QString &speakerName) +{ + QString headerString = "

Show "; + headerString += UnderlineString(speakerName); + headerString += " making the following interrogation statement:

"; + + return headerString; +} + +QString ShowInterrogationActionListItem::GetFooterString() +{ + Interrogation::ShowInterrogationAction *pShowInterrogationAction = GetShowInterrogationAction(); + QString footerString = ""; + + if (pShowInterrogationAction->conditionFlag.length() > 0) + { + footerString += "

Only show this statement if the flag "; + footerString += UnderlineString(pShowInterrogationAction->conditionFlag); + footerString += " is set.

"; + } + + if (pShowInterrogationAction->evidencePresentedActionsByEvidenceId.size() > 0) + { + footerString += "

Accept the following as valid evidence to present against this statement: "; + footerString += ConcatenateStringList(pShowInterrogationAction->evidencePresentedActionsByEvidenceId.keys()); + footerString += "

"; + } + + if (pShowInterrogationAction->partnerUsedActionsByPartnerId.size() > 0) + { + footerString += "

Accept the following as valid partners to use on this statement: "; + footerString += ConcatenateStringList(pShowInterrogationAction->partnerUsedActionsByPartnerId.keys()); + footerString += "

"; + } + + return footerString; +} + +EditorDialogContents * Interrogation::ExitInterrogationRepeatAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ExitInterrogationRepeatActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Interrogation::ExitInterrogationRepeatAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Interrogation::ExitInterrogationRepeatAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new ExitInterrogationRepeatActionListItem(this, indentLevel)); + return editorList; +} + +QString ExitInterrogationRepeatActionListItem::GetDisplayString() +{ + QString displayString = "Exit the current interrogation repeat loop."; + + return displayString; +} + +ExitInterrogationRepeatActionEditorDialogContents::ExitInterrogationRepeatActionEditorDialogContents(const Conversation::State &stateBeforeObject, Interrogation::ExitInterrogationRepeatAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pExitInterrogationRepeatAction = static_cast(pActionToEdit->Clone()); + } + else + { + pExitInterrogationRepeatAction = new Interrogation::ExitInterrogationRepeatAction(); + } + + pObject = pExitInterrogationRepeatAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + pLayout->addWidget(new QLabel("Exit the current interrogation repeat loop.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool ExitInterrogationRepeatActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Confrontation::Confrontation(Staging::Confrontation *pStagingConfrontation) : + Interrogation(pStagingConfrontation) +{ + +} + +void Confrontation::WriteToCaseXml(XmlWriter *pWriter) +{ + pWriter->StartElement("Confrontation"); + WriteToCaseXmlCore(pWriter); + pWriter->EndElement(); +} + +void Confrontation::WriteToCaseXmlCore(XmlWriter *pWriter) +{ + Interrogation::WriteToCaseXmlCore(pWriter); +} + +void Confrontation::PopulateActionFromStaging(QList &actions, const QList &stagingActions, int &indexInStagingActions, int initialIndexInFullList) +{ + Staging::Conversation::Action *pAction = stagingActions[indexInStagingActions]; + + switch (pAction->GetType()) + { + case Staging::ConversationActionType_SetParticipants: + { + Staging::Confrontation::SetParticipantsAction *pStagingSetParticipantsAction = static_cast(pAction); + + playerCharacterId = pStagingSetParticipantsAction->PlayerCharacterId; + opponentCharacterId = pStagingSetParticipantsAction->OpponentCharacterId; + } + break; + + case Staging::ConversationActionType_SetIconOffset: + { + Staging::Confrontation::SetIconOffsetAction *pStagingSetIconOffsetAction = static_cast(pAction); + + if (pStagingSetIconOffsetAction->IsPlayer) + { + playerIconOffset = pStagingSetIconOffsetAction->Offset; + } + else + { + opponentIconOffset = pStagingSetIconOffsetAction->Offset; + } + } + break; + + case Staging::ConversationActionType_SetHealth: + { + Staging::Confrontation::SetHealthAction *pStagingSetHealthAction = static_cast(pAction); + + if (pStagingSetHealthAction->IsPlayer) + { + playerInitialHealth = pStagingSetHealthAction->InitialHealth; + } + else + { + opponentInitialHealth = pStagingSetHealthAction->InitialHealth; + } + } + break; + + case Staging::ConversationActionType_BeginConfrontationTopicSelection: + { + Staging::Confrontation::BeginConfrontationTopicSelectionAction *pBeginConfrontationTopicSelectionAction = + static_cast(pAction); + + ConfrontationTopicSelectionAction *pConfrontationTopicSelectionAction = new ConfrontationTopicSelectionAction(pBeginConfrontationTopicSelectionAction); + + QList topicList; + topicList.append(pBeginConfrontationTopicSelectionAction->TopicList); + + if (pBeginConfrontationTopicSelectionAction->pInitialTopic != NULL) + { + topicList.append(pBeginConfrontationTopicSelectionAction->pInitialTopic); + } + + for (Staging::Confrontation::Topic *pStagingTopic : topicList) + { + QList topicStagingActions; + QList topicActions; + + int endIndex = -1; + + for (int j = pStagingTopic->ActionIndex; j < stagingActions.count(); j++) + { + Staging::Conversation::Action *pInnerStagingAction = stagingActions[j]; + + topicStagingActions.append(pInnerStagingAction); + + if (pInnerStagingAction->GetType() == Staging::ConversationActionType_EndConfrontationTopic) + { + endIndex = j; + break; + } + } + + if (endIndex < 0) + { + throw new MLIException("Expected an EndConfrontationTopicAction to follow a BeginConfrontationTopicAction."); + } + + PopulateActionsFromStaging(topicActions, topicStagingActions, pStagingTopic->ActionIndex - initialIndexInFullList); + pConfrontationTopicSelectionAction->AddTopic(pStagingTopic, topicActions, pStagingTopic == pBeginConfrontationTopicSelectionAction->pInitialTopic); + } + + if (pBeginConfrontationTopicSelectionAction->PlayerDefeatedIndex >= 0) + { + QList playerDefeatedStagingActions; + + int endIndex = -1; + + for (int j = pBeginConfrontationTopicSelectionAction->PlayerDefeatedIndex; j < stagingActions.count(); j++) + { + Staging::Conversation::Action *pInnerStagingAction = stagingActions[j]; + + playerDefeatedStagingActions.append(pInnerStagingAction); + + if (pInnerStagingAction->GetType() == Staging::ConversationActionType_EndPlayerDefeated) + { + endIndex = j; + break; + } + } + + if (endIndex < 0) + { + throw new MLIException("Expected an EndPlayerDefeatedAction to follow a BeginPlayerDefeatedAction."); + } + + PopulateActionsFromStaging(pConfrontationTopicSelectionAction->playerDefeatedActions, playerDefeatedStagingActions, pBeginConfrontationTopicSelectionAction->PlayerDefeatedIndex - initialIndexInFullList); + } + + actions.append(pConfrontationTopicSelectionAction); + indexInStagingActions = pBeginConfrontationTopicSelectionAction->EndActionIndex - 1; + } + break; + + case Staging::ConversationActionType_EnableTopic: + actions.append(new EnableTopicAction(static_cast(pAction))); + break; + + case Staging::ConversationActionType_BeginMultipleChoice: + { + Staging::Conversation::BeginMultipleChoiceAction *pStagingBeginMultipleChoiceAction = + static_cast(pAction); + + QList multipleChoiceActionHolder; + MultipleChoiceAction *pMultipleChoiceAction = NULL; + + Interrogation::PopulateActionFromStaging(multipleChoiceActionHolder, stagingActions, indexInStagingActions, initialIndexInFullList); + + if (multipleChoiceActionHolder.count() != 1 || (pMultipleChoiceAction = dynamic_cast(multipleChoiceActionHolder[0])) == NULL) + { + throw new MLIException("Expected to get a MultipleChoiceAction from a BeginMultipleChoiceAction in the stager."); + } + + // In V1 of the case engine, we didn't actually have RestartDecisionActions, so we get at what we meant by those + // by checking a MultipleChoiceAction for very specific options. + bool isRestartDecisionAction = + pMultipleChoiceAction->options.count() == 2 && + pMultipleChoiceAction->options[0]->text == QString("Try again - Restart confrontation") && + pMultipleChoiceAction->options[1]->text == QString("Give up - Back to title screen"); + + if (isRestartDecisionAction) + { + RestartDecisionAction *pRestartDecisionAction = new RestartDecisionAction(pStagingBeginMultipleChoiceAction); + pRestartDecisionAction->restartActions = pMultipleChoiceAction->options[0]->actions; + pRestartDecisionAction->quitActions = pMultipleChoiceAction->options[1]->actions; + + actions.append(pRestartDecisionAction); + } + else + { + actions.append(pMultipleChoiceAction); + } + } + break; + + case Staging::ConversationActionType_RestartConfrontation: + actions.append(new RestartConfrontationAction(static_cast(pAction))); + break; + + default: + Interrogation::PopulateActionFromStaging(actions, stagingActions, indexInStagingActions, initialIndexInFullList); + } +} + +Confrontation::Topic::Topic() +{ + isEnabledAtStart = false; +} + +Confrontation::Topic::Topic(QString id, QString name, bool isEnabledAtStart) +{ + this->id = id; + this->name = name; + this->isEnabledAtStart = isEnabledAtStart; +} + +Confrontation::Topic::Topic(Staging::Confrontation::Topic *pStagingTopic) + : Topic() +{ + id = pStagingTopic->Id; + name = pStagingTopic->Name; + isEnabledAtStart = pStagingTopic->IsEnabled; +} + +Confrontation::Topic::~Topic() +{ + for (Action *pAction : actions) + { + delete pAction; + } + + actions.clear(); +} + +Confrontation::Topic * Confrontation::Topic::Clone() +{ + Confrontation::Topic *pClonedTopic = new Confrontation::Topic(); + + pClonedTopic->id = id; + pClonedTopic->name = name; + pClonedTopic->isEnabledAtStart = isEnabledAtStart; + Conversation::Action::CloneActionList(pClonedTopic->actions, actions); + + return pClonedTopic; +} + +void Confrontation::Topic::AddActions(const QList &actions) +{ + this->actions.append(actions); +} + +void Confrontation::Topic::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, actions); +} + +void Confrontation::Topic::ExchangeListItemBaseOwnership(Topic *pOther) +{ + Conversation::Action::ExchangeListItemBaseOwnership(pOther->actions, actions); +} + +void Confrontation::Topic::UpdateAndCacheConversationState(State ¤tState) +{ + for (Action *pAction : actions) + { + pAction->UpdateAndCacheConversationState(currentState); + } +} + +void Confrontation::Topic::PreloadAudio() +{ + for (Action *pAction : actions) + { + pAction->PreloadAudio(); + } +} + +Confrontation::ConfrontationTopicSelectionAction::ConfrontationTopicSelectionAction() +{ + pInitialTopic = NULL; +} + +Confrontation::ConfrontationTopicSelectionAction::~ConfrontationTopicSelectionAction() +{ + for (Topic *pTopic : topics) + { + delete pTopic; + } + + topics.clear(); + + delete pInitialTopic; + pInitialTopic = NULL; + + for (Action *pAction : playerDefeatedActions) + { + delete pAction; + } + + playerDefeatedActions.clear(); +} + +EditorDialogContents * Confrontation::ConfrontationTopicSelectionAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new ConfrontationTopicSelectionActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Confrontation::ConfrontationTopicSelectionAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Confrontation::ConfrontationTopicSelectionAction *pAction = static_cast(pOther); + + delete pAction->pInitialTopic; + pAction->pInitialTopic = pInitialTopic != NULL ? pInitialTopic->Clone() : NULL; + + for (Topic *pTopic : pAction->topics) + { + delete pTopic; + } + + pAction->topics.clear(); + + for (Topic *pTopic : topics) + { + pAction->topics.push_back(pTopic->Clone()); + } + + Conversation::Action::CloneActionList(pAction->playerDefeatedActions, playerDefeatedActions); +} + +void Confrontation::ConfrontationTopicSelectionAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Confrontation::ConfrontationTopicSelectionAction *pAction = static_cast(pOther); + + for (Confrontation::Topic *pTopic : topics) + { + Confrontation::Topic *pOtherTopic = NULL; + + for (Confrontation::Topic *pTopicCandidate : pAction->topics) + { + if (pTopic->GetId() == pTopicCandidate->GetId()) + { + pOtherTopic = pTopicCandidate; + break; + } + } + + if (pOtherTopic != NULL) + { + pTopic->ExchangeListItemBaseOwnership(pOtherTopic); + } + } + + Conversation::Action::ExchangeListItemBaseOwnership(pAction->playerDefeatedActions, playerDefeatedActions); +} + +void Confrontation::ConfrontationTopicSelectionAction::AddTopic(Staging::Confrontation::Topic *pStagingTopic, const QList &actions, bool isInitialTopic) +{ + Topic *pTopic = new Topic(pStagingTopic); + pTopic->AddActions(actions); + + if (isInitialTopic) + { + pInitialTopic = pTopic; + } + else + { + topics.append(pTopic); + } +} + +void Confrontation::ConfrontationTopicSelectionAction::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + for (Topic *pTopic : topics) + { + pTopic->ReplaceAction(pNewAction, pOldAction); + } + + if (pInitialTopic != NULL) + { + pInitialTopic->ReplaceAction(pNewAction, pOldAction); + } + + Conversation::ReplaceAction(pNewAction, pOldAction, playerDefeatedActions); +} + +void Confrontation::ConfrontationTopicSelectionAction::UpdateAndCacheConversationState(State ¤tState) +{ + Conversation::Action::UpdateAndCacheConversationState(currentState); + + for (Topic *pTopic : topics) + { + State tempState = currentState.Clone(); + pTopic->UpdateAndCacheConversationState(tempState); + } + + if (pInitialTopic != NULL) + { + State tempState = currentState.Clone(); + pInitialTopic->UpdateAndCacheConversationState(tempState); + } + + State tempState = currentState.Clone(); + + for (Action *pAction : playerDefeatedActions) + { + pAction->UpdateAndCacheConversationState(tempState); + } +} + +void Confrontation::ConfrontationTopicSelectionAction::PreloadAudio() +{ + Conversation::Action::PreloadAudio(); + + for (Topic *pTopic : topics) + { + pTopic->PreloadAudio(); + } + + if (pInitialTopic != NULL) + { + pInitialTopic->PreloadAudio(); + } + + for (Action *pAction : playerDefeatedActions) + { + pAction->PreloadAudio(); + } +} + +QList *> Confrontation::ConfrontationTopicSelectionAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new ConfrontationTopicSelectionActionListItem(this, indentLevel)); + + if (pInitialTopic != NULL) + { + editorList.append(new TextDisplay("When the player first enters the confrontation:", indentLevel + 1)); + editorList.append(GetListItemsForActions(pInitialTopic->GetActions(), indentLevel + 2)); + } + + for (Topic *pTopic : topics) + { + editorList.append(new TextDisplay(QString("When the player selects \"") + pTopic->GetName() + "\":", indentLevel + 1)); + editorList.append(GetListItemsForActions(pTopic->GetActions(), indentLevel + 2)); + } + + editorList.append(new TextDisplay("If the player is defeated:", indentLevel + 1)); + editorList.append(GetListItemsForActions(playerDefeatedActions, indentLevel + 2)); + + return editorList; +} + +QString ConfrontationTopicSelectionActionListItem::GetDisplayString() +{ + QString displayString = "

Have the player select from the following confrontation topics:

"; + displayString += "

"; + + for (Confrontation::Topic *pTopic : pAction->topics) + { + displayString += QString("- ") + pTopic->GetName() + (pTopic->GetIsEnabledAtStart() ? " (enabled at the start)" : "") + "
"; + } + + displayString += "

"; + + if (pAction->pInitialTopic != NULL) + { + displayString += "

Also include an initial topic to play immediately as soon as the player enters the confrontation.

"; + } + + return displayString; +} + +ConfrontationTopicSelectionActionEditorDialogContents::ConfrontationTopicSelectionActionEditorDialogContents(const Conversation::State &stateBeforeObject, Confrontation::ConfrontationTopicSelectionAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pConfrontationTopicSelectionAction = static_cast(pActionToEdit->Clone()); + } + else + { + pConfrontationTopicSelectionAction = new Confrontation::ConfrontationTopicSelectionAction(); + } + + pObject = pConfrontationTopicSelectionAction; + + QVBoxLayout *pLayout = new QVBoxLayout(); + + QLabel *pHeaderLabel = new QLabel("Have the player select from the following confrontation topics:"); + pHeaderLabel->setWordWrap(true); + pLayout->addWidget(pHeaderLabel); + + MultipleSelectionWidget *pTopicSelector = new MultipleSelectionWidget(); + + if (pActionToEdit != NULL && pActionToEdit->topics.size() > 0) + { + QStringList strings; + + for (Confrontation::Topic *pTopic : pActionToEdit->topics) + { + strings.append(ConfrontationTopicMultipleSelectionSelectorWidget::CreateString(pTopic->GetId(), pTopic->GetName(), pTopic->GetIsEnabledAtStart())); + } + + pTopicSelector->SetSelections(strings); + } + else + { + QStringList stringList; + stringList.append(""); + + pTopicSelector->SetSelections(stringList); + } + + pLayout->addWidget(pTopicSelector); + + QCheckBox *pInitialTopicCheckBox = new QCheckBox("Include an initial topic"); + pLayout->addWidget(pInitialTopicCheckBox); + + if (pActionToEdit != NULL) + { + pInitialTopicCheckBox->setChecked(pActionToEdit->pInitialTopic != NULL); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + QObject::connect(pTopicSelector, SIGNAL(StringChanged(int,QString)), this, SLOT(TopicSelectorStringChanged(int,QString))); + QObject::connect(pTopicSelector, SIGNAL(StringAdded(int,QString)), this, SLOT(TopicSelectorStringAdded(int,QString))); + QObject::connect(pTopicSelector, SIGNAL(StringRemoved(int)), this, SLOT(TopicSelectorStringRemoved(int))); + QObject::connect(pInitialTopicCheckBox, SIGNAL(toggled(bool)), this, SLOT(InitialTopicCheckBoxToggled(bool))); +} + +bool ConfrontationTopicSelectionActionEditorDialogContents::ValidateFields() +{ + return true; +} + +void ConfrontationTopicSelectionActionEditorDialogContents::TopicSelectorStringChanged(int index, const QString &string) +{ + QString id; + QString name; + bool isEnabledAtStart; + + ConfrontationTopicMultipleSelectionSelectorWidget::ParseString(string, &id, &name, &isEnabledAtStart); + + pConfrontationTopicSelectionAction->topics[index]->SetId(id); + pConfrontationTopicSelectionAction->topics[index]->SetName(name); + pConfrontationTopicSelectionAction->topics[index]->SetIsEnabledAtStart(isEnabledAtStart); +} + +void ConfrontationTopicSelectionActionEditorDialogContents::TopicSelectorStringAdded(int index, const QString &string) +{ + QString id; + QString name; + bool isEnabledAtStart; + + ConfrontationTopicMultipleSelectionSelectorWidget::ParseString(string, &id, &name, &isEnabledAtStart); + pConfrontationTopicSelectionAction->topics.insert(index, new Confrontation::Topic(id, name, isEnabledAtStart)); +} + +void ConfrontationTopicSelectionActionEditorDialogContents::TopicSelectorStringRemoved(int index) +{ + Confrontation::Topic *pRemovedTopic = pConfrontationTopicSelectionAction->topics[index]; + pConfrontationTopicSelectionAction->topics.removeAt(index); + delete pRemovedTopic; +} + +void ConfrontationTopicSelectionActionEditorDialogContents::InitialTopicCheckBoxToggled(bool checked) +{ + if (checked && pConfrontationTopicSelectionAction->pInitialTopic == NULL) + { + pConfrontationTopicSelectionAction->pInitialTopic = new Confrontation::Topic(); + } + else if (!checked && pConfrontationTopicSelectionAction->pInitialTopic != NULL) + { + delete pConfrontationTopicSelectionAction->pInitialTopic; + pConfrontationTopicSelectionAction->pInitialTopic = NULL; + } +} + +Confrontation::EnableTopicAction::EnableTopicAction(Staging::Confrontation::EnableTopicAction *pEnableTopicAction) +{ + topicId = pEnableTopicAction->TopicId; +} + +EditorDialogContents * Confrontation::EnableTopicAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new EnableTopicActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Confrontation::EnableTopicAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Confrontation::EnableTopicAction *pAction = static_cast(pOther); + + pAction->topicId = topicId; +} + +QList *> Confrontation::EnableTopicAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new EnableTopicActionListItem(this, indentLevel)); + return editorList; +} + +QString EnableTopicActionListItem::GetDisplayString() +{ + QString displayString = "Enable the confrontation topic "; + displayString += UnderlineString(pAction->topicId); + displayString += "."; + + return displayString; +} + +EnableTopicActionEditorDialogContents::EnableTopicActionEditorDialogContents(const Conversation::State &stateBeforeObject, Confrontation::EnableTopicAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pEnableTopicAction = static_cast(pActionToEdit->Clone()); + } + else + { + pEnableTopicAction = new Confrontation::EnableTopicAction(); + } + + pObject = pEnableTopicAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Enable the confrontation topic ")); + + QComboBox *pConfrontationTopicComboBox = new QComboBox(); + pLayout->addWidget(pConfrontationTopicComboBox); + + if (pActionToEdit != NULL) + { + pConfrontationTopicComboBox->addItem(pEnableTopicAction->topicId); + } + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); + + if (pActionToEdit == NULL) + { + pEnableTopicAction->topicId = pConfrontationTopicComboBox->currentText(); + } + + QObject::connect(pConfrontationTopicComboBox, SIGNAL(currentTextChanged(QString)), this, SLOT(ConfrontationTopicComboBoxCurrentTextChanged(QString))); +} + +void EnableTopicActionEditorDialogContents::ConfrontationTopicComboBoxCurrentTextChanged(const QString &topicId) +{ + pEnableTopicAction->topicId = topicId; +} + +bool EnableTopicActionEditorDialogContents::ValidateFields() +{ + return true; +} + +Confrontation::RestartDecisionAction::~RestartDecisionAction() +{ + for (Action *pAction : restartActions) + { + delete pAction; + } + + restartActions.clear(); + + for (Action *pAction : quitActions) + { + delete pAction; + } + + quitActions.clear(); +} + +EditorDialogContents * Confrontation::RestartDecisionAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new RestartDecisionActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Confrontation::RestartDecisionAction::CopyPropertiesImpl(Conversation::Action *pOther, bool /*isForEdit*/) +{ + Confrontation::RestartDecisionAction *pAction = static_cast(pOther); + + Conversation::Action::CloneActionList(pAction->restartActions, restartActions); + Conversation::Action::CloneActionList(pAction->quitActions, quitActions); +} + +void Confrontation::RestartDecisionAction::ExchangeListItemBaseOwnershipImpl(Action *pOther) +{ + Confrontation::RestartDecisionAction *pAction = static_cast(pOther); + + Conversation::Action::ExchangeListItemBaseOwnership(pAction->restartActions, restartActions); + Conversation::Action::ExchangeListItemBaseOwnership(pAction->quitActions, quitActions); +} + +void Confrontation::RestartDecisionAction::ReplaceAction(Conversation::Action *pNewAction, Conversation::Action *pOldAction) +{ + Conversation::ReplaceAction(pNewAction, pOldAction, restartActions); + Conversation::ReplaceAction(pNewAction, pOldAction, quitActions); +} + +void Confrontation::RestartDecisionAction::UpdateAndCacheConversationState(State ¤tState) +{ + Conversation::Action::UpdateAndCacheConversationState(currentState); + + State tempState = currentState.Clone(); + + for (Action *pAction : restartActions) + { + pAction->UpdateAndCacheConversationState(tempState); + } + + tempState = currentState.Clone(); + + for (Action *pAction : quitActions) + { + pAction->UpdateAndCacheConversationState(tempState); + } +} + +void Confrontation::RestartDecisionAction::PreloadAudio() +{ + Conversation::Action::PreloadAudio(); + + for (Action *pAction : restartActions) + { + pAction->PreloadAudio(); + } + + for (Action *pAction : quitActions) + { + pAction->PreloadAudio(); + } +} + +QList *> Confrontation::RestartDecisionAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + + editorList.append(new RestartDecisionActionListItem(this, indentLevel)); + editorList.append(new TextDisplay("If the player restarts the confrontation:", indentLevel + 1)); + editorList.append(GetListItemsForActions(restartActions, indentLevel + 2)); + editorList.append(new TextDisplay("If the player quits the game:", indentLevel + 1)); + editorList.append(GetListItemsForActions(quitActions, indentLevel + 2)); + + return editorList; +} + +QString RestartDecisionActionListItem::GetDisplayString() +{ + QString displayString = "Prompt the player to either restart the confrontation or quit the game."; + + return displayString; +} + +RestartDecisionActionEditorDialogContents::RestartDecisionActionEditorDialogContents(const Conversation::State &stateBeforeObject, Confrontation::RestartDecisionAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pRestartDecisionAction = static_cast(pActionToEdit->Clone()); + } + else + { + pRestartDecisionAction = new Confrontation::RestartDecisionAction(); + } + + pObject = pRestartDecisionAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Prompt the player to either restart the confrontation or quit the game.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool RestartDecisionActionEditorDialogContents::ValidateFields() +{ + return true; +} + +EditorDialogContents * Confrontation::RestartConfrontationAction::CreateEditorDialogContents(const Conversation::State &stateBeforeObject, Conversation::Action *pActionToEdit) +{ + return new RestartConfrontationActionEditorDialogContents(stateBeforeObject, static_cast(pActionToEdit)); +} + +void Confrontation::RestartConfrontationAction::CopyPropertiesImpl(Action */*pOther*/, bool /*isForEdit*/) +{ +} + +QList *> Confrontation::RestartConfrontationAction::GetListItemsImpl(int indentLevel) +{ + QList *> editorList; + editorList.append(new RestartConfrontationActionListItem(this, indentLevel)); + return editorList; +} + +QString RestartConfrontationActionListItem::GetDisplayString() +{ + QString displayString = "Restart the current confrontation."; + + return displayString; +} + +RestartConfrontationActionEditorDialogContents::RestartConfrontationActionEditorDialogContents(const Conversation::State &stateBeforeObject, Confrontation::RestartConfrontationAction *pActionToEdit) + : EditorDialogContents(stateBeforeObject, pActionToEdit) +{ + if (pActionToEdit != NULL) + { + pRestartConfrontationAction = static_cast(pActionToEdit->Clone()); + } + else + { + pRestartConfrontationAction = new Confrontation::RestartConfrontationAction(); + } + + pObject = pRestartConfrontationAction; + + QHBoxLayout *pLayout = new QHBoxLayout(); + + pLayout->addWidget(new QLabel("Restart the current confrontation.")); + + pLayout->addStretch(1); + pLayout->setContentsMargins(0, 0, 0, 0); + pLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(pLayout); +} + +bool RestartConfrontationActionEditorDialogContents::ValidateFields() +{ + return true; +} + +ConversationSelector::ConversationSelector(QWidget *parent) + : QComboBox(parent) +{ + QObject::connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(CurrentIndexChanged(int))); +} + +QString ConversationSelector::GetId() +{ + return currentText(); +} + +void ConversationSelector::SetToId(const QString &id) +{ + int indexOfCurrentConversation = conversationIds.indexOf(QRegExp(id, Qt::CaseInsensitive)); + + if (indexOfCurrentConversation >= 0) + { + setCurrentIndex(indexOfCurrentConversation); + } + else + { + setCurrentIndex(0); + } +} + +void ConversationSelector::Reset(Encounter *pParentEncounter) +{ + conversationIds = CaseContent::GetInstance()->GetIds(pParentEncounter); + previousIndex = -1; + + clear(); + addItems(conversationIds); +} + +void ConversationSelector::CurrentIndexChanged(int currentIndex) +{ + previousIndex = currentIndex; + + if (currentIndex >= 0) + { + emit ConversationSelected(conversationIds[currentIndex]); + } + else + { + emit ConversationSelected(""); + } +} + +namespace ConversationPrivate +{ + QMap ActionTypeByComboBoxStringMap + { + { "Change character...", Conversation::ActionType::CharacterChange }, + { "Show dialog...", Conversation::ActionType::ShowDialog }, + { "Require the player to present evidence...", Conversation::ActionType::MustPresentEvidence }, + { "Set flag...", Conversation::ActionType::SetFlag }, + { "Branch on condition...", Conversation::ActionType::BranchOnCondition }, + { "Enable conversation...", Conversation::ActionType::EnableConversation }, + { "Enable evidence...", Conversation::ActionType::EnableEvidence }, + { "Update evidence...", Conversation::ActionType::UpdateEvidence }, + { "Disable evidence...", Conversation::ActionType::DisableEvidence }, + { "Enable cutscene...", Conversation::ActionType::EnableCutscene }, + { "Play background music...", Conversation::ActionType::PlayBgm }, + { "Pause background music...", Conversation::ActionType::PauseBgm }, + { "Resume background music...", Conversation::ActionType::ResumeBgm }, + { "Stop background music...", Conversation::ActionType::StopBgm }, + { "Play ambiance...", Conversation::ActionType::PlayAmbiance }, + { "Pause ambiance...", Conversation::ActionType::PauseAmbiance }, + { "Resume ambiance...", Conversation::ActionType::ResumeAmbiance }, + { "Stop ambiance...", Conversation::ActionType::StopAmbiance }, + { "Start animation...", Conversation::ActionType::StartAnimation }, + { "Stop animation...", Conversation::ActionType::StopAnimation }, + { "Set partner...", Conversation::ActionType::SetPartner }, + { "Go to the present-wrong-evidence branch...", Conversation::ActionType::GoToPresentWrongEvidence }, + { "Lock this conversation...", Conversation::ActionType::LockConversation }, + { "Exit this encounter...", Conversation::ActionType::ExitEncounter }, + { "Move to location...", Conversation::ActionType::MoveToLocation }, + { "Move to zoomed view...", Conversation::ActionType::MoveToZoomedView }, + { "End the current case...", Conversation::ActionType::EndCase }, + { "Display multiple choice options...", Conversation::ActionType::MultipleChoice }, + { "Exit multiple choice loop...", Conversation::ActionType::ExitMultipleChoice }, + { "Enable fast-forwarding...", Conversation::ActionType::EnableFastForward }, + { "Disable fast-forwarding...", Conversation::ActionType::DisableFastForward }, + { "Begin opponent's breakdown...", Conversation::ActionType::BeginBreakdown }, + { "End opponent's breakdown...", Conversation::ActionType::EndBreakdown }, + { "Repeat the following interrogation lines...", Conversation::ActionType::InterrogationRepeat }, + { "Show interrogation line...", Conversation::ActionType::ShowInterrogation }, + { "Exit the interrogation loop...", Conversation::ActionType::ExitInterrogationRepeat }, + { "Display the following confrontation topics...", Conversation::ActionType::ConfrontationTopicSelection }, + { "Enable confrontation topic...", Conversation::ActionType::EnableTopic }, + { "Ask the player whether they want to restart the confrontation...", Conversation::ActionType::RestartDecision }, + { "Restart the confrontation...", Conversation::ActionType::RestartConfrontation }, + }; + + typedef EditorDialogContents * (*PFNEDITORDIALOGCONTENTSCREATE)(const Conversation::State &, Conversation::Action *); + + QMap EditorDialogContentsCreationMethodsByTypeMap + { + { Conversation::ActionType::CharacterChange, Conversation::CharacterChangeAction::CreateEditorDialogContents }, + { Conversation::ActionType::SetFlag, Conversation::SetFlagAction::CreateEditorDialogContents }, + { Conversation::ActionType::BranchOnCondition, Conversation::BranchOnConditionAction::CreateEditorDialogContents }, + { Conversation::ActionType::ShowDialog, Conversation::ShowDialogAction::CreateEditorDialogContents }, + { Conversation::ActionType::MustPresentEvidence, Conversation::MustPresentEvidenceAction::CreateEditorDialogContents }, + { Conversation::ActionType::EnableConversation, Conversation::EnableConversationAction::CreateEditorDialogContents }, + { Conversation::ActionType::EnableEvidence, Conversation::EnableEvidenceAction::CreateEditorDialogContents }, + { Conversation::ActionType::UpdateEvidence, Conversation::UpdateEvidenceAction::CreateEditorDialogContents }, + { Conversation::ActionType::DisableEvidence, Conversation::DisableEvidenceAction::CreateEditorDialogContents }, + { Conversation::ActionType::EnableCutscene, Conversation::EnableCutsceneAction::CreateEditorDialogContents }, + { Conversation::ActionType::PlayBgm, Conversation::PlayBgmAction::CreateEditorDialogContents }, + { Conversation::ActionType::PauseBgm, Conversation::PauseBgmAction::CreateEditorDialogContents }, + { Conversation::ActionType::ResumeBgm, Conversation::ResumeBgmAction::CreateEditorDialogContents }, + { Conversation::ActionType::StopBgm, Conversation::StopBgmAction::CreateEditorDialogContents }, + { Conversation::ActionType::PlayAmbiance, Conversation::PlayAmbianceAction::CreateEditorDialogContents }, + { Conversation::ActionType::PauseAmbiance, Conversation::PauseAmbianceAction::CreateEditorDialogContents }, + { Conversation::ActionType::ResumeAmbiance, Conversation::ResumeAmbianceAction::CreateEditorDialogContents }, + { Conversation::ActionType::StopAmbiance, Conversation::StopAmbianceAction::CreateEditorDialogContents }, + { Conversation::ActionType::StartAnimation, Conversation::StartAnimationAction::CreateEditorDialogContents }, + { Conversation::ActionType::StopAnimation, Conversation::StopAnimationAction::CreateEditorDialogContents }, + { Conversation::ActionType::SetPartner, Conversation::SetPartnerAction::CreateEditorDialogContents }, + { Conversation::ActionType::GoToPresentWrongEvidence, Conversation::GoToPresentWrongEvidenceAction::CreateEditorDialogContents }, + { Conversation::ActionType::LockConversation, Conversation::LockConversationAction::CreateEditorDialogContents }, + { Conversation::ActionType::ExitEncounter, Conversation::ExitEncounterAction::CreateEditorDialogContents }, + { Conversation::ActionType::MoveToLocation, Conversation::MoveToLocationAction::CreateEditorDialogContents }, + { Conversation::ActionType::MoveToZoomedView, Conversation::MoveToZoomedViewAction::CreateEditorDialogContents }, + { Conversation::ActionType::EndCase, Conversation::EndCaseAction::CreateEditorDialogContents }, + { Conversation::ActionType::MultipleChoice, Conversation::MultipleChoiceAction::CreateEditorDialogContents }, + { Conversation::ActionType::ExitMultipleChoice, Conversation::ExitMultipleChoiceAction::CreateEditorDialogContents }, + { Conversation::ActionType::EnableFastForward, Conversation::EnableFastForwardAction::CreateEditorDialogContents }, + { Conversation::ActionType::DisableFastForward, Conversation::DisableFastForwardAction::CreateEditorDialogContents }, + { Conversation::ActionType::BeginBreakdown, Conversation::BeginBreakdownAction::CreateEditorDialogContents }, + { Conversation::ActionType::EndBreakdown, Conversation::EndBreakdownAction::CreateEditorDialogContents }, + { Conversation::ActionType::InterrogationRepeat, Interrogation::InterrogationRepeatAction::CreateEditorDialogContents }, + { Conversation::ActionType::ShowInterrogation, Interrogation::ShowInterrogationAction::CreateEditorDialogContents }, + { Conversation::ActionType::ExitInterrogationRepeat, Interrogation::ExitInterrogationRepeatAction::CreateEditorDialogContents }, + { Conversation::ActionType::ConfrontationTopicSelection, Confrontation::ConfrontationTopicSelectionAction::CreateEditorDialogContents }, + { Conversation::ActionType::EnableTopic, Confrontation::EnableTopicAction::CreateEditorDialogContents }, + { Conversation::ActionType::RestartDecision, Confrontation::RestartDecisionAction::CreateEditorDialogContents }, + { Conversation::ActionType::RestartConfrontation, Confrontation::RestartConfrontationAction::CreateEditorDialogContents }, + }; +} + +template <> +QString GetObjectDisplayName() +{ + return "conversation action"; +} + +template <> +QMap GetTypeByComboBoxStringMap() +{ + return ConversationPrivate::ActionTypeByComboBoxStringMap; +} + +template <> +QMap GetEditorDialogContentsCreationMethodsByTypeMap() +{ + return ConversationPrivate::EditorDialogContentsCreationMethodsByTypeMap; +} diff --git a/src/CaseCreator/CaseContent/Conversation.h b/src/CaseCreator/CaseContent/Conversation.h new file mode 100644 index 0000000..3bfc66c --- /dev/null +++ b/src/CaseCreator/CaseContent/Conversation.h @@ -0,0 +1,3539 @@ +#ifndef CONVERSATION_H +#define CONVERSATION_H + +#include +#include +#include +#include +#include + +#include "Character.h" +#include "Condition.h" +#include "CaseCreator/Utilities/Interfaces.h" +#include "CaseCreator/UIComponents/BaseTypes/ObjectListWidget.h" +#include "CaseCreator/UIComponents/TemplateHelpers/NewObjectDialog.h" + +#include "CaseCreator/CaseContent/Staging/Conversation.Staging.h" + +#include "../UIComponents/BaseTypes/MultipleSelectionWidget.h" + +#include "XmlStorableObject.h" + +#include "SharedUtils.h" +#include "MLIException.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +class Encounter; + +class QLabel; +class QComboBox; + +class ListItemTreeWidget; + +class EvidenceSelector; +class BackgroundMusicSelector; + +class CharacterSelectorMultipleSelectionSelectorWidget : public MultipleSelectionSelectorWidget +{ + Q_OBJECT + +public: + CharacterSelectorMultipleSelectionSelectorWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0) : MultipleSelectionSelectorWidget(parent, flags) { } + + virtual QString GetString() override; + virtual void SetToString(const QString &string) override; + virtual QWidget * CreateSelector() override; + +public slots: + void CharacterSelectorCharacterSelected(const QString &characterId); + +private: + CharacterSelector *pCharacterSelector; +}; + +class EvidenceSelectorMultipleSelectionSelectorWidget : public MultipleSelectionSelectorWidget +{ + Q_OBJECT + +public: + EvidenceSelectorMultipleSelectionSelectorWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0) : MultipleSelectionSelectorWidget(parent, flags) { } + + virtual QString GetString() override; + virtual void SetToString(const QString &string) override; + virtual QWidget * CreateSelector() override; + +public slots: + void EvidenceSelectorEvidenceSelected(const QString &evidenceId); + +private: + EvidenceSelector *pEvidenceSelector; +}; + +class LineEditMultipleSelectionSelectorWidget : public MultipleSelectionSelectorWidget +{ + Q_OBJECT + +public: + LineEditMultipleSelectionSelectorWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0) : MultipleSelectionSelectorWidget(parent, flags) { } + + virtual QString GetString() override; + virtual void SetToString(const QString &string) override; + virtual QWidget * CreateSelector() override; + +public slots: + void LineEditTextEdited(const QString &evidenceId); + +private: + QLineEdit *pLineEdit; +}; + +class ConfrontationTopicMultipleSelectionSelectorWidget : public MultipleSelectionSelectorWidget +{ + Q_OBJECT + +public: + ConfrontationTopicMultipleSelectionSelectorWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0) : MultipleSelectionSelectorWidget(parent, flags) { } + + virtual QString GetString() override; + virtual void SetToString(const QString &string) override; + virtual QWidget * CreateSelector() override; + + static QString CreateString(QString id, QString name, bool isEnabledAtStart); + static void ParseString(QString string, QString *pId, QString *pName, bool *pIsEnabledAtStart); + +public slots: + void NameLineEditTextEdited(const QString &evidenceId); + void IsEnabledCheckBoxToggled(bool isChecked); + +private: + QString GenerateString(); + + QLineEdit *pNameLineEdit; + QCheckBox *pIsEnabledCheckBox; +}; + +class ConversationSelector : public QComboBox +{ + Q_OBJECT + +public: + explicit ConversationSelector(QWidget *parent = 0); + + QString GetId(); + void SetToId(const QString &id); + + void Reset(Encounter *pParentEncounter); + +signals: + void ConversationSelected(const QString &conversationId); + +public slots: + void CurrentIndexChanged(int currentIndex); + +private: + QStringList conversationIds; + int previousIndex; +}; + +class ConversationActionListItemSlots : public QObject +{ + Q_OBJECT + +public: + explicit ConversationActionListItemSlots(QObject *parent = 0) : QObject(parent) {} + +signals: + void Selected(ConversationActionListItemSlots *pSender); +}; + +class ConversationDialogSlots : public QObject +{ + Q_OBJECT + +public: + explicit ConversationDialogSlots(QObject *parent = 0) : QObject(parent) {} + +public slots: + void HandleTimeout() + { + IncrementPositionOnTimeout(); + } + +protected: + virtual void IncrementPositionOnTimeout() = 0; +}; + +class Conversation : public XmlStorableObject +{ + BEGIN_XML_STORABLE_OBJECT(Conversation) + XML_STORABLE_TEXT(id) + XML_STORABLE_TEXT(name) + XML_STORABLE_CUSTOM_OBJECT_LIST(actions, Action::CreateFromXml) + END_XML_STORABLE_OBJECT() + +public: + class Action; + class DrawingView; + + class Dialog : public ConversationDialogSlots + { + public: + Dialog(); + ~Dialog(); + + void Reset(); + + void SetParsedDialog(const QString &parsedDialog) + { + this->parsedDialog = parsedDialog; + } + + void SetCharacters( + QString leftCharacterId, + QString leftCharacterInitialEmotionId, + QString rightCharacterId, + QString rightCharacterInitialEmotionId, + CharacterPosition speakerPosition, + QString speakingCharacterId) + { + this->leftCharacterId = leftCharacterId; + this->leftCharacterInitialEmotionId = leftCharacterInitialEmotionId; + this->rightCharacterId = rightCharacterId; + this->rightCharacterInitialEmotionId = rightCharacterInitialEmotionId; + this->speakerPosition = speakerPosition; + this->speakingCharacterId = speakingCharacterId; + } + + void SetAudio(QString dialogAudioFileName) + { + this->dialogAudioFileName = dialogAudioFileName; + } + + void AddSpeedChangePosition(int position, double newMillisecondsPerCharacterUpdate); + void AddEmotionChangePosition(int position, const QString &newEmotionId); + void AddEmotionOtherChangePosition(int position, const QString &newEmotionId); + void AddPausePosition(int position, double millisecondDuration); + void AddAudioPausePosition(int position, double millisecondDuration); + void AddMouthChangePosition(int position, bool mouthIsOn); + + void StartAside(int position); + void EndAside(int position); + void StartEmphasis(int position); + void EndEmphasis(int position); + + void PlaySound(int position, const QString &id); + + QStringList GetHtmlLines(int lastTextPosition = -1) const; + + void PreloadAudio(); + + void PlayOnDrawingView(Conversation::DrawingView *pDrawingView); + void StopPlaying(); + + private: + void IncrementPositionOnTimeout(); + + struct TextColorTransition + { + TextColorTransition(int position, QColor color, bool endsColor) + { + this->Position = position; + this->Color = color; + this->EndsColor = endsColor; + } + + int Position; + QColor Color; + bool EndsColor; + }; + + class DialogEvent + { + public: + DialogEvent(int position, Dialog *pOwningDialog) + { + this->position = position; + this->pOwningDialog = pOwningDialog; + } + + virtual ~DialogEvent() {} + + int GetPosition() { return position; } + + virtual void Raise() = 0; + + protected: + Dialog *pOwningDialog; + int position; + }; + + class SpeedChangeEvent : public DialogEvent + { + public: + SpeedChangeEvent(int position, Dialog *pOwningDialog, double newMillisecondsPerCharacterUpdate) + : DialogEvent(position, pOwningDialog) + { + this->newMillisecondsPerCharacterUpdate = newMillisecondsPerCharacterUpdate; + } + + void Raise() override; + + private: + double newMillisecondsPerCharacterUpdate; + }; + + class SpeakerEmotionChangeEvent : public DialogEvent + { + public: + SpeakerEmotionChangeEvent(int position, Dialog *pOwningDialog, const QString &newEmotionId) + : DialogEvent(position, pOwningDialog) + { + this->newEmotionId = newEmotionId; + } + + void Raise() override; + + private: + QString newEmotionId; + }; + + class OtherEmotionChangeEvent : public DialogEvent + { + public: + OtherEmotionChangeEvent(int position, Dialog *pOwningDialog, const QString &newEmotionId) + : DialogEvent(position, pOwningDialog) + { + this->newEmotionId = newEmotionId; + } + + void Raise() override; + + private: + QString newEmotionId; + }; + + class PauseEvent : public DialogEvent + { + public: + PauseEvent(int position, Dialog *pOwningDialog, int msDuration) + : DialogEvent(position, pOwningDialog) + { + this->msDuration = msDuration; + } + + void Raise() override; + + private: + int msDuration; + }; + + class AudioPauseEvent : public DialogEvent + { + public: + AudioPauseEvent(int position, Dialog *pOwningDialog, int msDuration) + : DialogEvent(position, pOwningDialog) + { + this->msDuration = msDuration; + } + + void Raise() override; + + private: + int msDuration; + }; + + class MouthChangeEvent : public DialogEvent + { + public: + MouthChangeEvent(int position, Dialog *pOwningDialog, bool isMouthOpen) + : DialogEvent(position, pOwningDialog) + { + this->isMouthOpen = isMouthOpen; + } + + void Raise() override; + + private: + bool isMouthOpen; + }; + + class PlaySoundEvent : public DialogEvent + { + public: + PlaySoundEvent(int position, Dialog *pOwningDialog, const QString &id) + : DialogEvent(position, pOwningDialog) + { + this->id = id; + } + + void Raise() override; + + private: + QString id; + }; + + QString parsedDialog; + QList events; + QList colorTransitions; + + QString leftCharacterId; + QString leftCharacterInitialEmotionId; + QString rightCharacterId; + QString rightCharacterInitialEmotionId; + CharacterPosition speakerPosition; + QString speakingCharacterId; + + Conversation::DrawingView *pDrawingViewForPlaying; + int currentPosition; + QString leftCharacterEmotionId; + QString rightCharacterEmotionId; + int millisecondsPerCharacterUpdate; + int mouthOffCount; + + QString dialogAudioFileName; + QStringList soundEffectIds; + + QTimer playTimer; + QElapsedTimer elapsedTimer; + qint64 elapsedTimeLeftOver; + int msPauseDuration; + int msAudioPauseDuration; + QList::iterator eventIterator; + }; + + Conversation() + { + pOwningEncounter = NULL; + } + + Conversation(QString id) : Conversation() + { + this->id = id; + } + +protected: + Conversation(Staging::Conversation *pStagingConversation); + +public: + virtual ~Conversation(); + + virtual void WriteToCaseXml(XmlWriter *pWriter); + virtual void WriteToCaseXmlCore(XmlWriter *pWriter); + + static Conversation * CreateFromStaging(Staging::Conversation *pStagingConversation) + { + Conversation *pConversation = new Conversation(pStagingConversation); + pConversation->PopulateActionsFromStaging(pConversation->actions, pStagingConversation->ActionList); + + return pConversation; + } + + static Conversation * CreateFromXml(XmlReader *pReader) + { + return new Conversation(pReader); + } + + static QString GetObjectAdditionString() { return QString("conversation"); } + static QString GetListTitle() { return QString("Conversations"); } + static bool GetIsMainList() { return false; } + static bool GetAllowsNewObjects() { return true; } + + void PopulateActionsFromStaging(QList &actions, const QList &stagingActions, int initialIndex = 0); + void UpdateAndCacheConversationStates(); + void ReplaceAction(Action *pNewAction, Action *pOldAction); + static void ReplaceAction(Action *pNewAction, Action *pOldAction, QList &actionListToSearch); + void PreloadAudio(); + +protected: + virtual void PopulateActionFromStaging(QList &actions, const QList &stagingActions, int &indexInStagingActions, int initialIndexInFullList); + +public: + QString GetId() { return this->id; } + QString GetName() { return this->name; } + QString GetDisplayName() { return this->name.length() > 0 ? this->name : this->id; } + + Encounter * GetOwningEncounter() { return pOwningEncounter; } + void SetOwningEncounter(Encounter *pOwningEncounter) { this->pOwningEncounter = pOwningEncounter; } + + class DrawingView : public IDrawingView + { + friend class Conversation; + + public: + DrawingView(Conversation *pConversation, IDrawable *pParent); + virtual ~DrawingView(); + + void DrawCore(QGraphicsScene *pScene, QList &addedItems); + void DrawChildren(QGraphicsScene *pScene); + void UpdateCore(); + void UpdateChildren(); + void ResetChildren(); + + void SetLeftCharacter(QString characterId, QString emotionId); + void SetRightCharacter(QString characterId, QString emotionId); + void SetDialog(CharacterPosition characterPosition, const QStringList &dialogHtmlLines, QString offscreenCharacterId, bool isTalking); + + private: + QGraphicsPixmapItem *pDialogBackgroundPixmapItem; + + QString currentLeftCharacterId; + QString currentLeftCharacterEmotionId; + QString currentRightCharacterId; + QString currentRightCharacterEmotionId; + + Character::DialogDrawingView *pLeftCharacterDialogDrawingView; + Character::DialogDrawingView *pRightCharacterDialogDrawingView; + + QGraphicsItemGroup *pDialogItemGroup; + QGraphicsOpacityEffect *pDialogItemGroupOpacityEffect; + QGraphicsPixmapItem *pCharacterNameTabPixmapItem; + QGraphicsSimpleTextItem *pCharacterNameSimpleTextItem; + + // Only five lines will actually fit on screen, + // so we only need to allocate five text items. + QGraphicsTextItem *pDialogLinesTextItems[5]; + }; + + Conversation::DrawingView * GetDrawingView(IDrawable *pParent) + { + Conversation::DrawingView *pDrawingView = new Conversation::DrawingView(this, pParent); + + if (actions.length() > 0) + { + actions.first()->PushToDrawingView(pDrawingView); + } + + return pDrawingView; + } + + class UnlockCondition : public XmlStorableObject + { + BEGIN_XML_STORABLE_OBJECT(UnlockCondition) + END_XML_STORABLE_OBJECT() + + public: + enum class Type + { + FlagSet = 0, + PartnerPresent = 1, + }; + + UnlockCondition() { } + virtual ~UnlockCondition() { } + + virtual UnlockCondition::Type GetType() = 0; + + virtual UnlockCondition * Clone() = 0; + virtual QString GetDisplayString() = 0; + + static UnlockCondition * LoadFromStagingObject(Staging::Conversation::UnlockCondition *pStagingUnlockCondition); + static UnlockCondition * CreateFromXml(XmlReader *pReader); + }; + + class FlagSetUnlockCondition : public UnlockCondition + { + friend class LockConversationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(FlagSetUnlockCondition, UnlockCondition) + XML_STORABLE_TEXT(flagId) + END_XML_STORABLE_OBJECT() + + public: + FlagSetUnlockCondition() { } + FlagSetUnlockCondition(const QString &flagId) { this->flagId = flagId; } + FlagSetUnlockCondition(Staging::Conversation::FlagSetUnlockCondition *pStagingFlagSetUnlockCondition); + virtual ~FlagSetUnlockCondition() { } + + virtual UnlockCondition::Type GetType() override { return UnlockCondition::Type::FlagSet; } + + virtual UnlockCondition * Clone() override; + virtual QString GetDisplayString() override; + + private: + QString flagId; + }; + + class PartnerPresentUnlockCondition : public UnlockCondition + { + friend class LockConversationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(PartnerPresentUnlockCondition, UnlockCondition) + XML_STORABLE_TEXT(partnerId) + END_XML_STORABLE_OBJECT() + + public: + PartnerPresentUnlockCondition() { } + PartnerPresentUnlockCondition(const QString &partnerId) { this->partnerId = partnerId; } + PartnerPresentUnlockCondition(Staging::Conversation::PartnerPresentUnlockCondition *pStagingPartnerPresentUnlockCondition); + virtual ~PartnerPresentUnlockCondition() { } + + virtual UnlockCondition::Type GetType() override { return UnlockCondition::Type::PartnerPresent; } + + virtual UnlockCondition * Clone() override; + virtual QString GetDisplayString() override; + + private: + QString partnerId; + }; + + class State + { + public: + State() + : leftCharacterId("") + , leftCharacterEmotionId("") + , rightCharacterId("") + , rightCharacterEmotionId("") + { + } + + State( + const QString &initialLeftCharacterId, + const QString &initialLeftCharacterEmotionId, + const QString &initialRightCharacterId, + const QString &initialRightCharacterEmotionId) + : leftCharacterId(initialLeftCharacterId) + , leftCharacterEmotionId(initialLeftCharacterEmotionId) + , rightCharacterId(initialRightCharacterId) + , rightCharacterEmotionId(initialRightCharacterEmotionId) + { + } + + State Clone() const + { + return State( + this->leftCharacterId, + this->leftCharacterEmotionId, + this->rightCharacterId, + this->rightCharacterEmotionId); + } + + QString& GetLeftCharacterId() { return this->leftCharacterId; } + QString& GetLeftCharacterEmotionId() { return this->leftCharacterEmotionId; } + QString& GetRightCharacterId() { return this->rightCharacterId; } + QString& GetRightCharacterEmotionId() { return this->rightCharacterEmotionId; } + + bool SetLeftCharacterId(const QString &id); + bool SetLeftCharacterEmotionId(const QString &id); + bool SetRightCharacterId(const QString &id); + bool SetRightCharacterEmotionId(const QString &id); + + private: + QString leftCharacterId; + QString leftCharacterEmotionId; + QString rightCharacterId; + QString rightCharacterEmotionId; + }; + + enum class ActionType + { + CharacterChange, + SetFlag, + BranchOnCondition, + ShowDialog, + MustPresentEvidence, + EnableConversation, + EnableEvidence, + UpdateEvidence, + DisableEvidence, + EnableCutscene, + PlayBgm, + PauseBgm, + ResumeBgm, + StopBgm, + PlayAmbiance, + PauseAmbiance, + ResumeAmbiance, + StopAmbiance, + StartAnimation, + StopAnimation, + SetPartner, + GoToPresentWrongEvidence, + LockConversation, + ExitEncounter, + MoveToLocation, + MoveToZoomedView, + EndCase, + MultipleChoice, + ExitMultipleChoice, + EnableFastForward, + DisableFastForward, + BeginBreakdown, + EndBreakdown, + InterrogationRepeat, + ShowInterrogation, + ExitInterrogationRepeat, + ConfrontationTopicSelection, + EnableTopic, + RestartDecision, + RestartConfrontation, + }; + + class Action : public XmlStorableObject, public ListItemObject + { + BEGIN_XML_STORABLE_OBJECT(Action) + END_XML_STORABLE_OBJECT() + + public: + Action() {} + virtual ~Action() {} + + virtual void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) = 0; + + virtual ActionType GetType() = 0; + + Action * Clone(); + + void CopyProperties(ListItemObject *pOther, bool isForEdit) override; + void ExchangeListItemBaseOwnership(ListItemObject *pOther) override; + State & GetStateDuringObject() override { return stateDuringAction; } + + ListItemBase * GetListItemBase() { return pListItemBase; } + void SetListItemBase(ListItemBase *pListItemBase) { this->pListItemBase = pListItemBase; } + + static void CloneActionList(QList &destination, const QList &source); + static void ExchangeListItemBaseOwnership(const QList &destination, const QList &source); + + static Action * CreateFromXml(XmlReader *pReader); + + static QList *> GetListItemsForActions(QList &actions, int indentLevel); + QList *> GetListItems(int indentLevel); + + virtual void ReplaceAction(Action */*pNewAction*/, Action */*pOldAction*/) + { + throw new MLIException("ReplaceAction() should never be called on an action that has no children."); + } + + virtual void UpdateAndCacheConversationState(State ¤tState) + { + stateDuringAction = currentState.Clone(); + } + + virtual void PreloadAudio() { } + + virtual bool CanPlay() { return false; } + virtual void PushToDrawingView(Conversation::DrawingView *pDrawingView); + virtual void PlayOnDrawingView(Conversation::DrawingView */*pDrawingView*/) { } + virtual void StopPlaying() { } + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) = 0; + virtual void ExchangeListItemBaseOwnershipImpl(Action */*pOther*/) { } + virtual QList *> GetListItemsImpl(int indentLevel) = 0; + + private: + State stateDuringAction; + ListItemBase *pListItemBase; + + public: + template + class ActionListItem : public ListItem + { + public: + explicit ActionListItem(TAction *pAction, int indentLevel) + : ListItem(indentLevel) + { + static_assert(is_base_of::value, "Conversation::Action::ListItem must have a subclass of Conversation::Action as its template type."); + + SetObjectImpl(pAction); + } + + protected: + Conversation::Action * GetObjectImpl() override { return pAction; } + + void SetObjectImpl(Conversation::Action *pAction) override + { + this->pAction = dynamic_cast(pAction); + + if (pAction != NULL) + { + pAction->SetListItemBase(this); + } + } + + TAction *pAction; + }; + }; + + class ActionWithNotification : public Action + { + BEGIN_DERIVED_XML_STORABLE_OBJECT(ActionWithNotification, Action) + XML_STORABLE_BOOL(shouldNotify) + XML_STORABLE_TEXT(rawNotificationText) + XML_STORABLE_TEXT(oldEvidenceId) + XML_STORABLE_TEXT(newEvidenceId) + XML_STORABLE_TEXT(partnerId) + XML_STORABLE_TEXT(locationWithCutsceneId) + XML_STORABLE_TEXT(cutsceneId) + END_XML_STORABLE_OBJECT() + + public: + ActionWithNotification(); + virtual ~ActionWithNotification() { } + + void AddNotification(Staging::Conversation::NotificationAction *pStagingNotificationAction); + + protected: + void CopyPropertiesImpl(Action *pOther, bool isForEdit); + + bool shouldNotify; + QString rawNotificationText; + QString oldEvidenceId; + QString newEvidenceId; + QString partnerId; + QString locationWithCutsceneId; + QString cutsceneId; + }; + + class CharacterChangeAction : public Action + { + friend class CharacterChangeActionListItem; + friend class CharacterChangeActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(CharacterChangeAction, Action) + XML_STORABLE_ENUM(position, CharacterPositionToString, StringToCharacterPosition) + XML_STORABLE_TEXT(characterIdToChangeTo) + XML_STORABLE_TEXT(initialEmotionId) + XML_STORABLE_TEXT(sfxId) + END_XML_STORABLE_OBJECT() + + public: + CharacterChangeAction(); + CharacterChangeAction(Staging::Conversation::CharacterChangeAction *pStagingCharacterChangeAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::CharacterChange; } + + virtual void UpdateAndCacheConversationState(State ¤tState) override; + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + CharacterPosition position; + QString characterIdToChangeTo; + QString initialEmotionId; + QString sfxId; + }; + + class SetFlagAction : public ActionWithNotification + { + friend class SetFlagActionListItem; + friend class SetFlagActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(SetFlagAction, ActionWithNotification) + XML_STORABLE_TEXT(flagId) + END_XML_STORABLE_OBJECT() + + public: + SetFlagAction() : ActionWithNotification() {} + SetFlagAction(Staging::Conversation::SetFlagAction *pStagingSetFlagAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::SetFlag; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString flagId; + }; + + class BranchOnConditionAction : public Action + { + friend class Conversation; + friend class BranchOnConditionActionListItem; + friend class BranchOnConditionActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(BranchOnConditionAction, Action) + XML_STORABLE_CUSTOM_OBJECT(pCondition, Condition::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT_LIST(trueActions, Action::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT_LIST(falseActions, Action::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + BranchOnConditionAction(); + BranchOnConditionAction(Staging::Conversation::BranchOnConditionAction *pStagingBranchOnConditionAction); + virtual ~BranchOnConditionAction(); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::BranchOnCondition; } + + virtual void ReplaceAction(Action *pNewAction, Action *pOldAction) override; + virtual void UpdateAndCacheConversationState(State ¤tState) override; + virtual void PreloadAudio() override; + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual void ExchangeListItemBaseOwnershipImpl(Action *pOther) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + Condition *pCondition; + QList trueActions; + QList falseActions; + }; + + class ShowDialogAction : public Action, public IDialogEventsOwner + { + friend class ShowDialogActionListItem; + friend class ShowDialogActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(ShowDialogAction, Action) + XML_STORABLE_ENUM(speakerPosition, CharacterPositionToString, StringToCharacterPosition) + XML_STORABLE_TEXT(characterId) + XML_STORABLE_TEXT(rawDialog) + XML_STORABLE_TEXT(voiceOverFilePath) + XML_STORABLE_INT(leadInTime) + XML_STORABLE_BOOL(shouldAdvanceAutomatically) + XML_STORABLE_INT(delayBeforeContinuing) + END_XML_STORABLE_OBJECT() + + public: + ShowDialogAction(); + ShowDialogAction(Staging::Conversation::ShowDialogAction *pStagingShowDialogAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::ShowDialog; } + + void InitializeDialog(); + + virtual void UpdateAndCacheConversationState(State ¤tState) override; + virtual void PreloadAudio() override; + + virtual bool CanPlay() { return true; } + virtual void PushToDrawingView(Conversation::DrawingView *pDrawingView) override; + virtual void PlayOnDrawingView(Conversation::DrawingView *pDrawingView) override; + virtual void StopPlaying() override; + + virtual void LoadElementsFromXml(XmlReader *pReader) + { + XmlStorableObject::LoadElementsFromXml(pReader); + InitializeDialog(); + } + + void AddSpeedChangePosition(int position, double newMillisecondsPerCharacterUpdate) override + { + parsedDialog.AddSpeedChangePosition(position, newMillisecondsPerCharacterUpdate); + } + + void AddEmotionChangePosition(int position, const QString &newEmotionId) override + { + parsedDialog.AddEmotionChangePosition(position, newEmotionId); + } + + void AddEmotionOtherChangePosition(int position, const QString &newEmotionId) override + { + parsedDialog.AddEmotionOtherChangePosition(position, newEmotionId); + } + + void AddPausePosition(int position, double millisecondDuration) override + { + parsedDialog.AddPausePosition(position, millisecondDuration); + } + + void AddAudioPausePosition(int position, double millisecondDuration) override + { + parsedDialog.AddAudioPausePosition(position, millisecondDuration); + } + + void AddMouthChangePosition(int position, bool mouthIsOn) override + { + parsedDialog.AddMouthChangePosition(position, mouthIsOn); + } + + void StartAside(int position) override + { + AddMouthChangePosition(position, false /* mouthIsOn */); + parsedDialog.StartAside(position); + } + + void EndAside(int position, int eventEnd, int parsedStringLength, QString *pStringToPrependOnNext) override + { + if (eventEnd + 1 == parsedStringLength) + { + *pStringToPrependOnNext = "{Mouth:On}"; + } + else + { + AddMouthChangePosition(position, true /* mouthIsOn */); + } + + parsedDialog.EndAside(position); + } + + void StartEmphasis(int position) override + { + parsedDialog.StartEmphasis(position); + } + + void EndEmphasis(int position) override + { + parsedDialog.EndEmphasis(position); + } + + void AddPlaySoundPosition(int position, const QString &id) override + { + parsedDialog.PlaySound(position, id); + } + + void AddShakePosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new ShakeEvent(position, this)); + } + + void AddScreenShakePosition(int /*position*/, double /*shakeIntensity*/) override + { + //this->dialogEventListOriginal.push_back(new ScreenShakeEvent(position, this, shakeIntensity)); + } + + void AddNextFramePosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new NextFrameEvent(position, this)); + } + + void AddPlayerDamagedPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new PlayerDamagedEvent(position, this)); + } + + void AddOpponentDamagedPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new OpponentDamagedEvent(position, this)); + } + + void AddPlayBgmPosition(int /*position*/, const QString &/*id*/) override + { + //this->dialogEventListOriginal.push_back(new PlayBgmEvent(position, this, id)); + } + + void AddPlayBgmPermanentlyPosition(int /*position*/, const QString &/*id*/) override + { + //this->dialogEventListOriginal.push_back(new PlayBgmEvent(position, this, id, true /* isPermanent */)); + } + + void AddStopBgmPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new StopBgmEvent(position, this, false /* isInstant */, false /* isPermanent */)); + } + + void AddStopBgmPermanentlyPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new StopBgmEvent(position, this, false /* isInstant */, true /* isPermanent */)); + } + + void AddStopBgmInstantlyPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new StopBgmEvent(position, this, true /* isInstant */, false /* isPermanent */)); + } + + void AddStopBgmInstantlyPermanentlyPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new StopBgmEvent(position, this, true /* isInstant */, true /* isPermanent */)); + } + + void AddZoomPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new ZoomEvent(position, this)); + } + + void AddEndZoomPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new EndZoomEvent(position, this)); + } + + void AddBeginBreakdownPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new BeginBreakdownEvent(position, this)); + } + + void AddEndBreakdownPosition(int /*position*/) override + { + //this->dialogEventListOriginal.push_back(new EndBreakdownEvent(position, this)); + } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + CharacterPosition speakerPosition; + QString characterId; + QString rawDialog; + Dialog parsedDialog; + QString voiceOverFilePath; + int leadInTime; + bool shouldAdvanceAutomatically; + int delayBeforeContinuing; + + State stateBeforeAction; + }; + + class MustPresentEvidenceAction : public ShowDialogAction + { + friend class Conversation; + friend class MustPresentEvidenceActionListItem; + friend class ShowDialogActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(MustPresentEvidenceAction, ShowDialogAction) + XML_STORABLE_TEXT_LIST(correctEvidenceIdList) + XML_STORABLE_CUSTOM_OBJECT_LIST(correctEvidencePresentedActions, Action::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT_LIST(wrongEvidencePresentedActions, Action::CreateFromXml) + XML_STORABLE_CUSTOM_OBJECT_LIST(endRequestedActions, Action::CreateFromXml) + XML_STORABLE_BOOL(canEndBeRequested) + END_XML_STORABLE_OBJECT() + + public: + MustPresentEvidenceAction(); + MustPresentEvidenceAction(Staging::Conversation::MustPresentEvidenceAction *pStagingMustPresentEvidenceAction); + virtual ~MustPresentEvidenceAction(); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::MustPresentEvidence; } + + virtual void ReplaceAction(Action *pNewAction, Action *pOldAction) override; + virtual void UpdateAndCacheConversationState(State ¤tState) override; + virtual void PreloadAudio() override; + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual void ExchangeListItemBaseOwnershipImpl(Action *pOther) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QList correctEvidenceIdList; + + QList correctEvidencePresentedActions; + QList wrongEvidencePresentedActions; + QList endRequestedActions; + + bool canEndBeRequested; + }; + + class EnableConversationAction : public ActionWithNotification + { + friend class EnableConversationActionListItem; + friend class EnableConversationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(EnableConversationAction, ActionWithNotification) + XML_STORABLE_TEXT(encounterId) + XML_STORABLE_TEXT(conversationId) + END_XML_STORABLE_OBJECT() + + public: + EnableConversationAction() : ActionWithNotification() { } + EnableConversationAction(Staging::Conversation::EnableConversationAction *pStagingEnableConversationAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::EnableConversation; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString encounterId; + QString conversationId; + }; + + class EnableEvidenceAction : public ActionWithNotification + { + friend class EnableEvidenceActionListItem; + friend class EnableEvidenceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(EnableEvidenceAction, ActionWithNotification) + END_XML_STORABLE_OBJECT() + + public: + EnableEvidenceAction() : ActionWithNotification() { } + EnableEvidenceAction(Staging::Conversation::EnableEvidenceAction *pStagingEnableEvidenceAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::EnableEvidence; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel); + }; + + class UpdateEvidenceAction : public ActionWithNotification + { + friend class UpdateEvidenceActionListItem; + friend class UpdateEvidenceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(UpdateEvidenceAction, ActionWithNotification) + END_XML_STORABLE_OBJECT() + + public: + UpdateEvidenceAction() : ActionWithNotification() { } + UpdateEvidenceAction(Staging::Conversation::UpdateEvidenceAction *pStagingUpdateEvidenceAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::UpdateEvidence; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class DisableEvidenceAction : public ActionWithNotification + { + friend class DisableEvidenceActionListItem; + friend class DisableEvidenceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(DisableEvidenceAction, ActionWithNotification) + END_XML_STORABLE_OBJECT() + + public: + DisableEvidenceAction() : ActionWithNotification() { } + DisableEvidenceAction(Staging::Conversation::DisableEvidenceAction *pStagingDisableEvidenceAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::DisableEvidence; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class EnableCutsceneAction : public ActionWithNotification + { + friend class EnableCutsceneActionListItem; + friend class EnableCutsceneActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(EnableCutsceneAction, ActionWithNotification) + END_XML_STORABLE_OBJECT() + + public: + EnableCutsceneAction() : ActionWithNotification() { } + EnableCutsceneAction(Staging::Conversation::EnableCutsceneAction *pStagingEnableCutsceneAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::EnableCutscene; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class PlayBgmAction : public Action + { + friend class PlayBgmActionListItem; + friend class PlayBgmActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(PlayBgmAction, Action) + XML_STORABLE_TEXT(bgmId) + XML_STORABLE_BOOL(preserveBgm) + END_XML_STORABLE_OBJECT() + + public: + PlayBgmAction(); + PlayBgmAction(Staging::Conversation::PlayBgmAction *pStagingPlayBgmAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::PlayBgm; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString bgmId; + bool preserveBgm; + }; + + class PauseBgmAction : public Action + { + friend class PauseBgmActionListItem; + friend class PauseBgmActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(PauseBgmAction, Action) + END_XML_STORABLE_OBJECT() + + public: + PauseBgmAction() : Action() {} + PauseBgmAction(Staging::Conversation::PauseBgmAction */*pStagingPauseBgmAction*/) : PauseBgmAction() {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::PauseBgm; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class ResumeBgmAction : public Action + { + friend class ResumeBgmActionListItem; + friend class ResumeBgmActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(ResumeBgmAction, Action) + END_XML_STORABLE_OBJECT() + + public: + ResumeBgmAction() : Action() {} + ResumeBgmAction(Staging::Conversation::ResumeBgmAction */*pStagingResumeBgmAction*/) : ResumeBgmAction() {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::ResumeBgm; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class StopBgmAction : public Action + { + friend class StopBgmActionListItem; + friend class StopBgmActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(StopBgmAction, Action) + XML_STORABLE_BOOL(isInstant) + XML_STORABLE_BOOL(preserveBgm) + END_XML_STORABLE_OBJECT() + + public: + StopBgmAction(); + StopBgmAction(Staging::Conversation::StopBgmAction *pStagingStopBgmAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::StopBgm; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + bool isInstant; + bool preserveBgm; + }; + + class PlayAmbianceAction : public Action + { + friend class PlayAmbianceActionListItem; + friend class PlayAmbianceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(PlayAmbianceAction, Action) + XML_STORABLE_TEXT(ambianceSfxId) + XML_STORABLE_BOOL(preserveAmbiance) + END_XML_STORABLE_OBJECT() + + public: + PlayAmbianceAction(); + PlayAmbianceAction(Staging::Conversation::PlayAmbianceAction *pStagingPlayAmbianceAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::PlayAmbiance; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString ambianceSfxId; + bool preserveAmbiance; + }; + + class PauseAmbianceAction : public Action + { + friend class PauseAmbianceActionListItem; + friend class PauseAmbianceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(PauseAmbianceAction, Action) + END_XML_STORABLE_OBJECT() + + public: + PauseAmbianceAction() : Action() {} + PauseAmbianceAction(Staging::Conversation::PauseAmbianceAction */*pStagingPauseAmbianceAction*/) : PauseAmbianceAction() {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::PauseAmbiance; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class ResumeAmbianceAction : public Action + { + friend class ResumeAmbianceActionListItem; + friend class ResumeAmbianceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(ResumeAmbianceAction, Action) + END_XML_STORABLE_OBJECT() + + public: + ResumeAmbianceAction() : Action() {} + ResumeAmbianceAction(Staging::Conversation::ResumeAmbianceAction */*pStagingResumeAmbianceAction*/) : ResumeAmbianceAction() {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::ResumeAmbiance; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class StopAmbianceAction : public Action + { + friend class StopAmbianceActionListItem; + friend class StopAmbianceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(StopAmbianceAction, Action) + XML_STORABLE_BOOL(isInstant) + XML_STORABLE_BOOL(preserveAmbiance) + END_XML_STORABLE_OBJECT() + + public: + StopAmbianceAction(); + StopAmbianceAction(Staging::Conversation::StopAmbianceAction *pStagingStopAmbianceAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::StopAmbiance; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + bool isInstant; + bool preserveAmbiance; + }; + + class StartAnimationAction : public Action + { + friend class StartAnimationActionListItem; + friend class StartAnimationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(StartAnimationAction, Action) + XML_STORABLE_TEXT(animationId) + END_XML_STORABLE_OBJECT() + + public: + StartAnimationAction() : Action() {} + StartAnimationAction(Staging::Conversation::StartAnimationAction *pStartAnimationAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::StartAnimation; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString animationId; + }; + + class StopAnimationAction : public Action + { + friend class StopAnimationActionListItem; + friend class StopAnimationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(StopAnimationAction, Action) + END_XML_STORABLE_OBJECT() + + public: + StopAnimationAction() : Action() {} + StopAnimationAction(Staging::Conversation::StopAnimationAction */*pStopAnimationAction*/) {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::StopAnimation; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class SetPartnerAction : public ActionWithNotification + { + friend class SetPartnerActionListItem; + friend class SetPartnerActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(SetPartnerAction, ActionWithNotification) + END_XML_STORABLE_OBJECT() + + public: + SetPartnerAction() : ActionWithNotification() {} + SetPartnerAction(Staging::Conversation::SetPartnerAction *pSetPartnerAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::SetPartner; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class GoToPresentWrongEvidenceAction : public Action + { + friend class GoToPresentWrongEvidenceActionListItem; + friend class GoToPresentWrongEvidenceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(GoToPresentWrongEvidenceAction, Action) + END_XML_STORABLE_OBJECT() + + public: + GoToPresentWrongEvidenceAction() : Action() {} + GoToPresentWrongEvidenceAction(Staging::Conversation::GoToPresentWrongEvidenceAction */*pGoToPresentWrongEvidenceAction*/) {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::GoToPresentWrongEvidence; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class LockConversationAction : public Action + { + friend class LockConversationActionListItem; + friend class LockConversationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(LockConversationAction, Action) + XML_STORABLE_CUSTOM_OBJECT(pUnlockCondition, UnlockCondition::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + LockConversationAction(); + LockConversationAction(Staging::Conversation::LockConversationAction *pLockConversationAction); + virtual ~LockConversationAction(); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::LockConversation; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + UnlockCondition *pUnlockCondition; + }; + + class ExitEncounterAction : public Action + { + friend class ExitEncounterActionListItem; + friend class ExitEncounterActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(ExitEncounterAction, Action) + END_XML_STORABLE_OBJECT() + + public: + ExitEncounterAction() : Action() {} + ExitEncounterAction(Staging::Conversation::ExitEncounterAction */*pExitEncounterAction*/) {} + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::ExitEncounter; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + }; + + class MoveToLocationAction : public Action + { + friend class MoveToLocationActionListItem; + friend class MoveToLocationActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(MoveToLocationAction, Action) + XML_STORABLE_TEXT(newAreaId) + XML_STORABLE_TEXT(newLocationId) + XML_STORABLE_TEXT(transitionId) + END_XML_STORABLE_OBJECT() + + public: + MoveToLocationAction() : Action() {} + MoveToLocationAction(Staging::Conversation::MoveToLocationAction *pMoveToLocationAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::MoveToLocation; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString newAreaId; + QString newLocationId; + QString transitionId; + }; + + class MoveToZoomedViewAction : public Action + { + friend class MoveToZoomedViewActionListItem; + friend class MoveToZoomedViewActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(MoveToZoomedViewAction, Action) + XML_STORABLE_TEXT(zoomedViewId) + END_XML_STORABLE_OBJECT() + + public: + MoveToZoomedViewAction() : Action() {} + MoveToZoomedViewAction(Staging::Conversation::MoveToZoomedViewAction *pMoveToZoomedViewAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::MoveToZoomedView; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + QString zoomedViewId; + }; + + class EndCaseAction : public Action + { + friend class EndCaseActionListItem; + friend class EndCaseActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(EndCaseAction, Action) + XML_STORABLE_BOOL(completesCase) + END_XML_STORABLE_OBJECT() + + public: + EndCaseAction(); + EndCaseAction(Staging::Conversation::EndCaseAction *pEndCaseAction); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::EndCase; } + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + bool completesCase; + }; + + class MultipleChoiceAction : public Action + { + friend class Confrontation; + friend class MultipleChoiceActionListItem; + friend class MultipleChoiceActionEditorDialogContents; + + BEGIN_DERIVED_XML_STORABLE_OBJECT(MultipleChoiceAction, Action) + XML_STORABLE_CUSTOM_OBJECT_LIST(options, Option::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + MultipleChoiceAction() : Action() {} + MultipleChoiceAction(Staging::Conversation::BeginMultipleChoiceAction */*pBeginMultipleChoiceAction*/) : MultipleChoiceAction() {} + virtual ~MultipleChoiceAction(); + + void WriteToCaseXml(XmlWriter *pWriter, int& currentActionIndex) override; + + virtual ActionType GetType() override { return ActionType::MultipleChoice; } + + void AddOption(const QString &text, const QList &actions); + + static EditorDialogContents * CreateEditorDialogContents(const Conversation::State &stateBeforeAction, Conversation::Action *pActionToEdit); + + protected: + virtual void CopyPropertiesImpl(Action *pOther, bool isForEdit) override; + virtual void ExchangeListItemBaseOwnershipImpl(Action *pOther) override; + virtual QList *> GetListItemsImpl(int indentLevel) override; + + private: + class Option : public XmlStorableObject + { + friend class Confrontation; + + BEGIN_XML_STORABLE_OBJECT(Option) + XML_STORABLE_TEXT(text) + XML_STORABLE_CUSTOM_OBJECT_LIST(actions, Action::CreateFromXml) + END_XML_STORABLE_OBJECT() + + public: + Option() {} + Option(const QString &text); + Option(const QString &text, const QList &actions); + virtual ~Option(); + + Option * Clone(); + + QString GetText() { return text; } + void SetText(const QString &text) { this->text = text; } + + QList & GetActions() { return actions; } + + void ReplaceAction(Action *pNewAction, Action *pOldAction); + void ExchangeListItemBaseOwnership(Option *pOther); + + static Option * CreateFromXml(XmlReader *pReader) + { + return new Option(pReader); + } + + private: + QString text; + QList actions; + }; + + QList