Modified world info sidebar so it handles DKCR much more smoothly

This commit is contained in:
Aruki 2017-05-14 01:49:15 -06:00
parent b45eaae106
commit 77cda50153
3 changed files with 209 additions and 78 deletions

View File

@ -102,14 +102,17 @@ void CWorldInfoSidebar::OnWorldTreeClicked(QModelIndex Index)
QModelIndex RealIndex = mProxyModel.mapToSource(Index); QModelIndex RealIndex = mProxyModel.mapToSource(Index);
// Fill in world info // Fill in world info
mpUI->WorldInfoWidget->setHidden(false);
CWorld *pWorld = mModel.WorldForIndex(RealIndex); CWorld *pWorld = mModel.WorldForIndex(RealIndex);
mpUI->WorldNameLabel->setText( TO_QSTRING(pWorld->InGameName()) ); mpUI->WorldInfoWidget->setHidden( pWorld == nullptr );
mpUI->WorldSelector->SetResource(pWorld);
mpUI->WorldNameSelector->SetResource(pWorld->NameString()); if (pWorld)
mpUI->DarkWorldNameSelector->SetResource(pWorld->DarkNameString()); {
mpUI->SkySelector->SetResource(pWorld->DefaultSkybox()); mpUI->WorldNameLabel->setText( TO_QSTRING(pWorld->InGameName()) );
mpUI->WorldSelector->SetResource(pWorld);
mpUI->WorldNameSelector->SetResource(pWorld->NameString());
mpUI->DarkWorldNameSelector->SetResource(pWorld->DarkNameString());
mpUI->SkySelector->SetResource(pWorld->DefaultSkybox());
}
// Fill in area info // Fill in area info
bool IsArea = !mModel.IndexIsWorld(RealIndex); bool IsArea = !mModel.IndexIsWorld(RealIndex);
@ -117,7 +120,7 @@ void CWorldInfoSidebar::OnWorldTreeClicked(QModelIndex Index)
if (IsArea) if (IsArea)
{ {
int AreaIndex = mModel.AreaIndexForIndex(RealIndex); int AreaIndex = Editor()->CurrentGame() == eReturns ? 0 : mModel.AreaIndexForIndex(RealIndex);
mpUI->AreaNameLabel->setText( TO_QSTRING(pWorld->AreaInGameName(AreaIndex)) ); mpUI->AreaNameLabel->setText( TO_QSTRING(pWorld->AreaInGameName(AreaIndex)) );
mpUI->AreaSelector->SetResource( pWorld->AreaResourceID(AreaIndex) ); mpUI->AreaSelector->SetResource( pWorld->AreaResourceID(AreaIndex) );
mpUI->AreaNameLineEdit->setText( TO_QSTRING(pWorld->AreaInternalName(AreaIndex)) ); mpUI->AreaNameLineEdit->setText( TO_QSTRING(pWorld->AreaInternalName(AreaIndex)) );
@ -140,9 +143,20 @@ void CWorldInfoSidebar::OnWorldTreeDoubleClicked(QModelIndex Index)
if (!mModel.IndexIsWorld(RealIndex)) if (!mModel.IndexIsWorld(RealIndex))
{ {
CWorld *pWorld = mModel.WorldForIndex(RealIndex); TResPtr<CWorld> pWorld = mModel.WorldForIndex(RealIndex);
int AreaIndex = mModel.AreaIndexForIndex(RealIndex); int AreaIndex = mModel.AreaIndexForIndex(RealIndex);
gpEdApp->WorldEditor()->SetArea(pWorld, AreaIndex);
// Validate area actually exists... DKCR has worlds that contain areas that don't exist
CAssetID AreaAssetID = pWorld->AreaResourceID(AreaIndex);
if (gpResourceStore->IsResourceRegistered(AreaAssetID))
{
gpEdApp->WorldEditor()->SetArea(pWorld, AreaIndex);
}
else
{
UICommon::ErrorMsg(Editor(), "The MREA asset associated with this area doesn't exist!");
}
} }
} }

View File

@ -3,6 +3,7 @@
#include "CWorldEditor.h" #include "CWorldEditor.h"
#include "UICommon.h" #include "UICommon.h"
#include <Core/GameProject/CGameProject.h> #include <Core/GameProject/CGameProject.h>
#include <Core/GameProject/CResourceIterator.h>
#include <QIcon> #include <QIcon>
CWorldTreeModel::CWorldTreeModel(CWorldEditor *pEditor) CWorldTreeModel::CWorldTreeModel(CWorldEditor *pEditor)
@ -14,7 +15,7 @@ CWorldTreeModel::CWorldTreeModel(CWorldEditor *pEditor)
int CWorldTreeModel::rowCount(const QModelIndex& rkParent) const int CWorldTreeModel::rowCount(const QModelIndex& rkParent) const
{ {
if (!rkParent.isValid()) return mWorldList.size(); if (!rkParent.isValid()) return mWorldList.size();
else if (IndexIsWorld(rkParent)) return WorldForIndex(rkParent)->NumAreas(); else if (IndexIsWorld(rkParent)) return mWorldList[rkParent.row()].Areas.size();
else return 0; else return 0;
} }
@ -49,54 +50,46 @@ QVariant CWorldTreeModel::data(const QModelIndex& rkIndex, int Role) const
{ {
if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole) if (Role == Qt::DisplayRole || Role == Qt::ToolTipRole)
{ {
CWorld *pWorld = WorldForIndex(rkIndex); const SWorldInfo& rkInfo = WorldInfoForIndex(rkIndex);
// World // World
if (IndexIsWorld(rkIndex)) if (IndexIsWorld(rkIndex))
{ {
QString WorldName = TO_QSTRING( pWorld->Name() );
CStringTable *pWorldNameString = pWorld->NameString();
// For Corruption worlds, we swap the columns around. This is because Corruption's in-game world names // For Corruption worlds, we swap the columns around. This is because Corruption's in-game world names
// are often missing, confusing, or just straight-up inaccurate, which makes the internal name a better // are often missing, confusing, or just straight-up inaccurate, which makes the internal name a better
// means of telling worlds apart. // means of telling worlds apart.
u32 InternalNameCol = (pWorld->Game() == eCorruption ? 0 : 1); // For DKCR worlds, we only display the world name in the first column.
u32 InternalNameCol = (gpEdApp->ActiveProject()->Game() >= eCorruption ? 0 : 1);
// Internal name
if (rkIndex.column() == InternalNameCol) if (rkIndex.column() == InternalNameCol)
return WorldName; return rkInfo.WorldName;
// In-Game name
else else
{ {
if (pWorldNameString) if (rkInfo.pWorld)
return TO_QSTRING( pWorldNameString->String("ENGL", 0) ); return TO_QSTRING( rkInfo.pWorld->InGameName() );
else else
return WorldName; return "";
} }
} }
// Area // Area
else else
{ {
u32 AreaIndex = AreaIndexForIndex(rkIndex); CWorld *pWorld = WorldForIndex(rkIndex);
ASSERT(AreaIndex >= 0 && AreaIndex < pWorld->NumAreas()); int AreaIndex = AreaIndexForIndex(rkIndex);
ASSERT(pWorld);
CAssetID AreaAssetID = pWorld->AreaResourceID(AreaIndex); TString AreaInternalName = pWorld->AreaInternalName(AreaIndex);
CResourceEntry *pAreaEntry = pWorld->Entry()->ResourceStore()->FindEntry(AreaAssetID); TString AreaInGameName = (gpEdApp->ActiveProject()->Game() == eReturns ? pWorld->InGameName() : pWorld->AreaInGameName( AreaIndexForIndex(rkIndex) ));
ASSERT(pAreaEntry);
QString AreaAssetName = TO_QSTRING( pAreaEntry->Name() );
CStringTable *pAreaNameString = pWorld->AreaName(AreaIndex);
// Return name
if (rkIndex.column() == 1) if (rkIndex.column() == 1)
return AreaAssetName; return TO_QSTRING(AreaInternalName);
else else
{ return TO_QSTRING(AreaInGameName);
if (pAreaNameString)
return TO_QSTRING( pAreaNameString->String("ENGL", 0) );
else
return "!!" + AreaAssetName;
}
} }
} }
@ -115,9 +108,6 @@ QVariant CWorldTreeModel::data(const QModelIndex& rkIndex, int Role) const
else if (Role == Qt::FontRole) else if (Role == Qt::FontRole)
{ {
CWorld *pWorld = WorldForIndex(rkIndex);
ASSERT(pWorld);
QFont Font; QFont Font;
int PointSize = Font.pointSize() + 2; int PointSize = Font.pointSize() + 2;
@ -125,16 +115,25 @@ QVariant CWorldTreeModel::data(const QModelIndex& rkIndex, int Role) const
{ {
PointSize += 1; PointSize += 1;
CWorld *pWorld = WorldForIndex(rkIndex); const SWorldInfo& rkInfo = WorldInfoForIndex(rkIndex);
if (gpEdApp->WorldEditor()->ActiveWorld() == pWorld) CWorld *pActiveWorld = gpEdApp->WorldEditor()->ActiveWorld();
Font.setBold(true);
if (pActiveWorld)
{
EGame Game = gpEdApp->ActiveProject()->Game();
bool IsActiveWorld = (Game <= eCorruption && rkInfo.pWorld == pActiveWorld) ||
(Game == eReturns && rkInfo.Areas.contains(pActiveWorld->Entry()));
if (IsActiveWorld)
Font.setBold(true);
}
} }
else else
{ {
CResourceEntry *pEntry = AreaEntryForIndex(rkIndex); CResourceEntry *pEntry = AreaEntryForIndex(rkIndex);
ASSERT(pEntry);
if (pEntry->IsLoaded()) if (pEntry && pEntry->IsLoaded())
{ {
if (gpEdApp->WorldEditor()->ActiveArea() == pEntry->Resource()) if (gpEdApp->WorldEditor()->ActiveArea() == pEntry->Resource())
Font.setBold(true); Font.setBold(true);
@ -164,26 +163,52 @@ QVariant CWorldTreeModel::headerData(int Section, Qt::Orientation Orientation, i
bool CWorldTreeModel::IndexIsWorld(const QModelIndex& rkIndex) const bool CWorldTreeModel::IndexIsWorld(const QModelIndex& rkIndex) const
{ {
return AreaIndexForIndex(rkIndex) == 0xFFFF; int AreaIndex = (int) rkIndex.internalId() & 0xFFFF;
} return (AreaIndex == 0xFFFF);
CWorld* CWorldTreeModel::WorldForIndex(const QModelIndex& rkIndex) const
{
int WorldIndex = (rkIndex.internalId() >> 16) & 0xFFFF;
return mWorldList[WorldIndex].pWorld;
} }
int CWorldTreeModel::AreaIndexForIndex(const QModelIndex& rkIndex) const int CWorldTreeModel::AreaIndexForIndex(const QModelIndex& rkIndex) const
{ {
int InternalID = (int) rkIndex.internalId(); if (gpEdApp->ActiveProject()->Game() == eReturns)
return (InternalID & 0xFFFF); return 0;
else
{
int InternalID = (int) rkIndex.internalId();
return (InternalID & 0xFFFF);
}
}
CWorld* CWorldTreeModel::WorldForIndex(const QModelIndex& rkIndex) const
{
ASSERT(rkIndex.isValid());
const SWorldInfo& rkInfo = WorldInfoForIndex(rkIndex);
if (gpEdApp->ActiveProject()->Game() == eReturns && !IndexIsWorld(rkIndex))
{
int AreaIndex = (int) rkIndex.internalId() & 0xFFFF;
CResourceEntry *pEntry = rkInfo.Areas[AreaIndex];
return pEntry ? (CWorld*) pEntry->Load() : nullptr;
}
else
return rkInfo.pWorld;
} }
CResourceEntry* CWorldTreeModel::AreaEntryForIndex(const QModelIndex& rkIndex) const CResourceEntry* CWorldTreeModel::AreaEntryForIndex(const QModelIndex& rkIndex) const
{ {
ASSERT(rkIndex.isValid() && !IndexIsWorld(rkIndex)); ASSERT(rkIndex.isValid() && !IndexIsWorld(rkIndex));
const SWorldInfo& rkInfo = mWorldList[rkIndex.parent().row()]; CWorld *pWorld = WorldForIndex(rkIndex);
return rkInfo.Areas[rkIndex.row()]; int AreaIndex = AreaIndexForIndex(rkIndex);
CAssetID AreaID;
if (pWorld) AreaID = pWorld->AreaResourceID(AreaIndex);
return gpResourceStore->FindEntry(AreaID);
}
const CWorldTreeModel::SWorldInfo& CWorldTreeModel::WorldInfoForIndex(const QModelIndex& rkIndex) const
{
int WorldIndex = ((int) rkIndex.internalId() >> 16) & 0xFFFF;
return mWorldList[WorldIndex];
} }
// ************ SLOTS ************ // ************ SLOTS ************
@ -194,43 +219,125 @@ void CWorldTreeModel::OnProjectChanged(CGameProject *pProj)
if (pProj) if (pProj)
{ {
std::list<CAssetID> WorldIDs; if (pProj->Game() != eReturns)
pProj->GetWorldList(WorldIDs);
QList<CAssetID> QWorldIDs = QList<CAssetID>::fromStdList(WorldIDs);
foreach (const CAssetID& rkID, QWorldIDs)
{ {
CResourceEntry *pEntry = pProj->ResourceStore()->FindEntry(rkID); // Metroid Prime series; fetch all world assets
std::list<CAssetID> WorldIDs;
pProj->GetWorldList(WorldIDs);
QList<CAssetID> QWorldIDs = QList<CAssetID>::fromStdList(WorldIDs);
if (pEntry) foreach (const CAssetID& rkID, QWorldIDs)
{ {
TResPtr<CWorld> pWorld = pEntry->Load(); CResourceEntry *pEntry = pProj->ResourceStore()->FindEntry(rkID);
if (pWorld) if (pEntry)
{ {
SWorldInfo Info; TResPtr<CWorld> pWorld = pEntry->Load();
Info.pWorld = pWorld;
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++) if (pWorld)
{ {
CAssetID AreaID = pWorld->AreaResourceID(iArea); SWorldInfo Info;
CResourceEntry *pAreaEntry = pWorld->Entry()->ResourceStore()->FindEntry(AreaID); Info.WorldName = TO_QSTRING( pWorld->Name() );
ASSERT(pAreaEntry); Info.pWorld = pWorld;
Info.Areas << pAreaEntry;
}
mWorldList << Info; // Add areas
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++)
{
CAssetID AreaID = pWorld->AreaResourceID(iArea);
CResourceEntry *pAreaEntry = pWorld->Entry()->ResourceStore()->FindEntry(AreaID);
ASSERT(pAreaEntry);
Info.Areas << pAreaEntry;
}
mWorldList << Info;
}
} }
} }
// Sort in alphabetical order for MP3
if (pProj->Game() >= eCorruption)
{
qSort(mWorldList.begin(), mWorldList.end(), [](const SWorldInfo& rkA, const SWorldInfo& rkB) -> bool {
return (rkA.WorldName.toUpper() < rkB.WorldName.toUpper());
});
}
} }
// Sort in alphabetical order for MP3 // DKCR - Get worlds from areas.lst
if (pProj->Game() == eCorruption) else
{ {
qSort(mWorldList.begin(), mWorldList.end(), [](const SWorldInfo& rkLeft, const SWorldInfo& rkRight) -> bool { TString AreaListPath = pProj->DiscDir(false) + "areas.lst";
return (rkLeft.pWorld->Name().ToUpper() < rkRight.pWorld->Name().ToUpper());
// I really need a good text stream class at some point
FILE* pAreaList = fopen(*AreaListPath, "r");
SWorldInfo *pInfo = nullptr;
std::set<CAssetID> UsedWorlds;
while (!feof(pAreaList))
{
char LineBuffer[256];
memset(LineBuffer, 0, 256);
fgets(LineBuffer, 256, pAreaList);
TString Line(LineBuffer);
CAssetID WorldID;
TString WorldName;
u32 IDSplit = Line.IndexOf(' ');
if (IDSplit != -1)
{
// Get world ID
TString IDString = (IDSplit == -1 ? "" : Line.SubString(2, IDSplit - 2));
WorldID = CAssetID::FromString(IDString);
// Get world name
TString WorldPath = (IDSplit == -1 ? "" : Line.SubString(IDSplit + 1, Line.Size() - IDSplit - 1));
u32 UnderscoreIdx = WorldPath.IndexOf('_');
u32 WorldDirEnd = WorldPath.IndexOf("\\/", UnderscoreIdx);
if (UnderscoreIdx != -1 && WorldDirEnd != -1)
WorldName = WorldPath.SubString(UnderscoreIdx + 1, WorldDirEnd - UnderscoreIdx - 1);
}
if (WorldID.IsValid() && !WorldName.IsEmpty())
{
CResourceEntry *pEntry = gpResourceStore->FindEntry(WorldID);
if (pEntry)
{
QString WorldNameQ = TO_QSTRING(WorldName);
if (!pInfo || pInfo->WorldName != WorldNameQ)
{
mWorldList << SWorldInfo();
pInfo = &mWorldList.back();
pInfo->WorldName = WorldNameQ;
}
pInfo->Areas << pEntry;
UsedWorlds.insert(pEntry->ID());
}
}
}
fclose(pAreaList);
// Add remaining worlds to FrontEnd world
mWorldList.prepend( SWorldInfo() );
pInfo = &mWorldList.front();
pInfo->WorldName = "FrontEnd";
for (TResourceIterator<eWorld> It; It; ++It)
{
if (UsedWorlds.find(It->ID()) == UsedWorlds.end())
pInfo->Areas << *It;
}
// Sort FrontEnd world
qSort( pInfo->Areas.begin(), pInfo->Areas.end(), [](CResourceEntry *pA, CResourceEntry *pB) -> bool {
return pA->UppercaseName() < pB->UppercaseName();
}); });
} }
} }
endResetModel(); endResetModel();

View File

@ -6,12 +6,19 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
class CWorldEditor; class CWorldEditor;
struct STreeArea
{
CAssetID WorldID;
int AreaIndex;
};
class CWorldTreeModel : public QAbstractItemModel class CWorldTreeModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
struct SWorldInfo struct SWorldInfo
{ {
QString WorldName;
TResPtr<CWorld> pWorld; TResPtr<CWorld> pWorld;
QList<CResourceEntry*> Areas; QList<CResourceEntry*> Areas;
}; };
@ -28,10 +35,13 @@ public:
QVariant headerData(int Section, Qt::Orientation Orientation, int Role) const; QVariant headerData(int Section, Qt::Orientation Orientation, int Role) const;
bool IndexIsWorld(const QModelIndex& rkIndex) const; bool IndexIsWorld(const QModelIndex& rkIndex) const;
CWorld* WorldForIndex(const QModelIndex& rkIndex) const;
int AreaIndexForIndex(const QModelIndex& rkIndex) const; int AreaIndexForIndex(const QModelIndex& rkIndex) const;
CWorld* WorldForIndex(const QModelIndex& rkIndex) const;
CResourceEntry* AreaEntryForIndex(const QModelIndex& rkIndex) const; CResourceEntry* AreaEntryForIndex(const QModelIndex& rkIndex) const;
protected:
const SWorldInfo& WorldInfoForIndex(const QModelIndex& rkIndex) const;
public slots: public slots:
void OnProjectChanged(CGameProject *pProj); void OnProjectChanged(CGameProject *pProj);
void OnMapChanged(); void OnMapChanged();