openclonk/src/object/C4ObjectList.cpp

995 lines
23 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, Matthes Bender
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
/* Dynamic object list */
#include <C4Include.h>
#include <C4ObjectList.h>
#include <C4DefList.h>
#include <C4Object.h>
#include <C4Application.h>
#include <C4GraphicsResource.h>
#include <C4Game.h>
#include <C4GameObjects.h>
static const C4ObjectLink NULL_LINK = { NULL, NULL, NULL };
C4ObjectList::C4ObjectList(): FirstIter(0)
{
Default();
}
C4ObjectList::C4ObjectList(const C4ObjectList &List): FirstIter(0)
{
Default();
Copy(List);
}
C4ObjectList::~C4ObjectList()
{
Clear();
}
void C4ObjectList::Clear()
{
C4ObjectLink *cLnk,*nextLnk;
for (cLnk=First; cLnk; cLnk=nextLnk)
{ nextLnk=cLnk->Next; delete cLnk; }
First=Last=NULL;
if (pEnumerated)
{
delete pEnumerated;
pEnumerated=NULL;
}
for (iterator* it = FirstIter; it; it = it->Next)
{
it->link = NULL_LINK;
}
}
const int MaxTempListID = 500;
C4ID TempListID[MaxTempListID];
C4ID C4ObjectList::GetListID(int32_t dwCategory, int Index) const
{
int clid;
C4ObjectLink *clnk;
C4Def *cdef;
// Create a temporary list of all id's and counts
for (clid=0; clid<MaxTempListID; clid++) TempListID[clid]=C4ID::None;
for (clnk=First; clnk && clnk->Obj; clnk=clnk->Next)
if (clnk->Obj->Status)
if ((dwCategory==C4D_All) || ( (cdef=C4Id2Def(clnk->Obj->Def->id)) && (cdef->Category & dwCategory) ))
for (clid=0; clid<MaxTempListID; clid++)
{
// Already there
if (TempListID[clid]==clnk->Obj->Def->id) break;
// End of list, add id
if (TempListID[clid]==C4ID::None) { TempListID[clid]=clnk->Obj->Def->id; break; }
}
// Returns indexed id
if (Inside(Index,0,MaxTempListID-1)) return TempListID[Index];
return C4ID::None;
}
int C4ObjectList::ListIDCount(int32_t dwCategory) const
{
int clid;
C4ObjectLink *clnk;
C4Def *cdef;
// Create a temporary list of all id's and counts
for (clid=0; clid<MaxTempListID; clid++) TempListID[clid]=C4ID::None;
for (clnk=First; clnk && clnk->Obj; clnk=clnk->Next)
if (clnk->Obj->Status)
if ((dwCategory==C4D_All) || ( (cdef=C4Id2Def(clnk->Obj->Def->id)) && (cdef->Category & dwCategory) ))
for (clid=0; clid<MaxTempListID; clid++)
{
// Already there
if (TempListID[clid]==clnk->Obj->Def->id) break;
// End of list, add id
if (TempListID[clid]==C4ID::None) { TempListID[clid]=clnk->Obj->Def->id; break; }
}
// Count different id's
for (clid=0; clid<MaxTempListID; clid++)
if (TempListID[clid]==C4ID::None)
return clid;
return MaxTempListID;
}
bool C4ObjectList::Add(C4Object *nObj, SortType eSort, C4ObjectList *pLstSorted)
{
C4ObjectLink *nLnk;
if (!nObj || !nObj->Def || !nObj->Status) return false;
#ifdef _DEBUG
if (eSort==stMain)
{
CheckCategorySort();
if (pLstSorted)
assert(CheckSort(pLstSorted));
}
#endif
// dbg: don't do double links
assert (!GetLink(nObj));
// no self-sort
assert(pLstSorted != this);
// Allocate new link
if (!(nLnk=new C4ObjectLink)) return false;
// Set link
nLnk->Obj=nObj;
// Search insert position (default: end of list)
C4ObjectLink *cLnk = NULL, *cPrev = Last;
// Should sort?
if (eSort == stReverse)
{
// reverse sort: Add to beginning of list
cLnk = First; cPrev = NULL;
}
else if (eSort)
{
// Sort override? Leave default as is.
bool fUnsorted = nObj->Unsorted;
if (!fUnsorted)
{
// Sort by master list?
if (pLstSorted)
{
cPrev = NULL; cLnk = First;
while(cLnk && (!cLnk->Obj->Status || cLnk->Obj->Unsorted)) cLnk = cLnk->Next;
#ifndef _DEBUG
if(cLnk)
#endif
{
C4ObjectLink* cLnk2;
for(cLnk2 = pLstSorted->First; cLnk2; cLnk2 = cLnk2->Next)
{
if(cLnk2->Obj->Status && !cLnk2->Obj->Unsorted)
{
if(cLnk2->Obj == nObj)
{
assert(!cLnk || cLnk->Obj != nObj);
break;
}
if(cLnk && cLnk2->Obj == cLnk->Obj)
{
cPrev = cLnk;
cLnk = cLnk->Next;
while(cLnk && (!cLnk->Obj->Status || cLnk->Obj->Unsorted)) cLnk = cLnk->Next;
#ifndef _DEBUG
if(!cLnk) break;
#endif
}
}
}
assert(cLnk2 != NULL);
}
}
else
{
// No master list: Find successor by matching Plane / id
// Sort by matching Plane/id is necessary for inventory shifting.
// It is not done for static back to allow multiobject outside structure.
// Unsorted objects are ignored in comparison.
if (!(nObj->Category & C4D_StaticBack))
for (cPrev=NULL,cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status && !cLnk->Obj->Unsorted)
{
if (cLnk->Obj->GetPlane() == nObj->GetPlane())
if (cLnk->Obj->id == nObj->id)
break;
cPrev=cLnk;
}
// Find successor by relative category
if(!cLnk)
for (cPrev=NULL, cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status && !cLnk->Obj->Unsorted)
{
if (cLnk->Obj->GetPlane() <= nObj->GetPlane())
break;
cPrev=cLnk;
}
}
cLnk = cPrev ? cPrev->Next : First;
}
}
assert(!cPrev || cPrev->Next == cLnk);
assert(!cLnk || cLnk->Prev == cPrev);
// Insert new link after predecessor
InsertLink(nLnk, cPrev);
#ifdef _DEBUG
// Debug: Check sort
if (eSort == stMain)
{
CheckCategorySort();
if (pLstSorted)
assert(CheckSort(pLstSorted));
}
#endif
// Add mass
Mass+=nObj->Mass;
return true;
}
bool C4ObjectList::Remove(C4Object *pObj)
{
C4ObjectLink *cLnk;
// Find link
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj==pObj) break;
if (!cLnk) return false;
// Fix iterators
for (iterator * i = FirstIter; i; i = i->Next)
{
// adjust pointers of internal link field
if (i->link.Prev == cLnk)
{
i->link.Prev = cLnk->Prev;
}
else if (i->link.Next == cLnk)
{
i->link.Next = cLnk->Next;
}
else if (i->link.Obj == cLnk->Obj)
{
i->link.Obj = nullptr;
}
}
// Remove link from list
RemoveLink(cLnk);
// Deallocate link
delete cLnk;
// Remove mass
Mass-=pObj->Mass; if (Mass<0) Mass=0;
#if defined(_DEBUG)
if (GetLink(pObj)) BREAKPOINT_HERE;
#endif
return true;
}
C4Object* C4ObjectList::Find(C4Def * def, int owner, DWORD dwOCF)
{
C4ObjectLink *cLnk;
// Find link and object
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
if (cLnk->Obj->Def==def)
if ((owner==ANY_OWNER) || (cLnk->Obj->Owner==owner))
if (dwOCF & cLnk->Obj->OCF)
return cLnk->Obj;
return NULL;
}
C4Object* C4ObjectList::FindOther(C4ID id, int owner)
{
C4ObjectLink *cLnk;
// Find link and object
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
if (cLnk->Obj->Def->id!=id)
if ((owner==ANY_OWNER) || (cLnk->Obj->Owner==owner))
return cLnk->Obj;
return NULL;
}
C4Object* C4ObjectList::GetObject(int Index) const
{
int cIdx;
C4ObjectLink *cLnk;
// Find link and object
for (cLnk=First,cIdx=0; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
{
if (cIdx==Index) return cLnk->Obj;
cIdx++;
}
return NULL;
}
const C4ObjectLink* C4ObjectList::GetLink(const C4Object *pObj) const
{
if (!pObj) return NULL;
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj==pObj)
return cLnk;
return NULL;
}
int C4ObjectList::ObjectCount(C4ID id) const
{
C4ObjectLink *cLnk;
int iCount=0;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status && (id==C4ID::None || cLnk->Obj->Def->id==id))
iCount++;
return iCount;
}
int C4ObjectList::MassCount()
{
C4ObjectLink *cLnk;
int iMass=0;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
iMass+=cLnk->Obj->Mass;
Mass=iMass;
return iMass;
}
int C4ObjectList::ClearPointers(C4Object *pObj)
{
int rval=0;
// Clear all primary list pointers
while (Remove(pObj)) rval++;
// Clear all sub pointers
C4Object *cobj; C4ObjectLink *clnk;
for (clnk=First; clnk && (cobj=clnk->Obj); clnk=clnk->Next)
cobj->ClearPointers(pObj);
return rval;
}
void C4ObjectList::Draw(C4TargetFacet &cgo, int iPlayer, int MinPlane, int MaxPlane)
{
C4ObjectLink * clnk, * first;
for (first=Last; first; first=first->Prev)
if (first->Obj->GetPlane() >= MinPlane)
break;
// Draw objects (base)
for (clnk=first; clnk; clnk=clnk->Prev)
{
if (first->Obj->GetPlane() > MaxPlane)
break;
if (clnk->Obj->Category & C4D_Foreground)
continue;
clnk->Obj->Draw(cgo, iPlayer);
}
// Draw objects (top face)
for (clnk=first; clnk; clnk=clnk->Prev)
{
if (first->Obj->GetPlane() > MaxPlane)
break;
if (clnk->Obj->Category & C4D_Foreground)
continue;
clnk->Obj->DrawTopFace(cgo, iPlayer);
}
}
void C4ObjectList::DrawIfCategory(C4TargetFacet &cgo, int iPlayer, uint32_t dwCat, bool fInvert)
{
C4ObjectLink *clnk;
// Draw objects (base)
for (clnk=Last; clnk; clnk=clnk->Prev)
if (!(clnk->Obj->Category & dwCat) == fInvert)
clnk->Obj->Draw(cgo, iPlayer);
// Draw objects (top face)
for (clnk=Last; clnk; clnk=clnk->Prev)
if (!(clnk->Obj->Category & dwCat) == fInvert)
clnk->Obj->DrawTopFace(cgo, iPlayer);
}
bool C4ObjectList::IsContained(const C4Object *pObj) const
{
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj==pObj)
return true;
return false;
}
bool C4ObjectList::IsClear() const
{
return (ObjectCount()==0);
}
bool C4ObjectList::DenumeratePointers()
{
if (!pEnumerated) return false;
// Denumerate all object pointers
for (std::list<int32_t>::const_iterator pNum = pEnumerated->begin(); pNum != pEnumerated->end(); ++pNum)
Add(::Objects.ObjectPointer(*pNum), stNone); // Add to tail, unsorted
// Delete old list
delete pEnumerated; pEnumerated = NULL;
return true;
}
bool C4ObjectList::Write(char *szTarget)
{
char ostr[25];
szTarget[0]=0;
C4ObjectLink *cLnk;
for (cLnk=First; cLnk && cLnk->Obj; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
{
sprintf(ostr,"%d;",cLnk->Obj->Number);
SAppend(ostr,szTarget);
}
return true;
}
void C4ObjectList::Denumerate(C4ValueNumbers * numbers)
{
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
cLnk->Obj->Denumerate(numbers);
}
void C4ObjectList::CompileFunc(StdCompiler *pComp, bool fSkipPlayerObjects, C4ValueNumbers * numbers)
{
// "Object" section count
int32_t iObjCnt = ObjectCount();
pComp->Value(mkNamingCountAdapt(iObjCnt, "Object"));
if (pComp->isDecompiler())
{
// skipping player objects would screw object counting in non-naming compilers
assert(!fSkipPlayerObjects || pComp->hasNaming());
// Decompile all objects in reverse order
for (C4ObjectLink *pPos = Last; pPos; pPos = pPos->Prev)
if (pPos->Obj->Status)
if (!fSkipPlayerObjects || !pPos->Obj->IsUserPlayerObject())
pComp->Value(mkNamingAdapt(mkParAdapt(*pPos->Obj, numbers), "Object"));
}
else
{
// FIXME: Check that no PlayerObjects are loaded when fSkipPlayerObjects is true
// i.e. that loading and saving was done with the same flag.
// Remove previous data
Clear();
// Load objects, add them to the list.
for (int i = 0; i < iObjCnt; i++)
{
C4Object *pObj = NULL;
try
{
pComp->Value(mkNamingAdapt(mkParAdapt(mkPtrAdaptNoNull(pObj), numbers), "Object"));
Add(pObj, stReverse);
}
catch (StdCompiler::Exception *pExc)
{
// Failsafe object loading: If an error occurs during object loading, just skip that object and load the next one
if (!pExc->Pos.getLength())
LogF("ERROR: Object loading: %s", pExc->Msg.getData());
else
LogF("ERROR: Object loading(%s): %s", pExc->Pos.getData(), pExc->Msg.getData());
delete pExc;
}
}
}
}
void C4ObjectList::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers)
{
// (Re)create list
delete pEnumerated; pEnumerated = new std::list<int32_t>();
// Decompiling: Build list
if (!pComp->isCompiler())
for (C4ObjectLink *pPos = First; pPos; pPos = pPos->Next)
if (pPos->Obj->Status)
pEnumerated->push_back(pPos->Obj->Number);
// Compile list
pComp->Value(mkSTLContainerAdapt(*pEnumerated, StdCompiler::SEP_SEP2));
// Decompiling: Delete list
if (!pComp->isCompiler())
{ delete pEnumerated; pEnumerated = NULL; }
// Compiling: Nothing to do - list will be denumerated later
}
StdStrBuf C4ObjectList::GetNameList(C4DefList &rDefs) const
{
int cpos,idcount;
C4ID c_id;
C4Def *cdef;
StdStrBuf Buf;
for (cpos=0; (c_id=GetListID(C4D_All,cpos)); cpos++)
if ((cdef=rDefs.ID2Def(c_id)))
{
idcount=ObjectCount(c_id);
if (cpos>0) Buf.Append(", ");
Buf.AppendFormat("%dx %s",idcount,cdef->GetName());
}
return Buf;
}
StdStrBuf C4ObjectList::GetDataString()
{
StdStrBuf Output;
// Compose info text by selected object(s)
switch (ObjectCount())
{
// No selection
case 0:
Output = LoadResStr("IDS_CNS_NOOBJECT");
break;
// One selected object
case 1:
Output.Take(GetObject()->GetDataString());
break;
// Multiple selected objects
default:
Output.Format(LoadResStr("IDS_CNS_MULTIPLEOBJECTS"),ObjectCount());
break;
}
return Output;
}
bool C4ObjectList::ValidateOwners()
{
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
cLnk->Obj->ValidateOwner();
return true;
}
bool C4ObjectList::AssignInfo()
{
// the list seems to be traced backwards here, to ensure crew objects are added in correct order
// (or semi-correct, because this will work only if the crew order matches the main object list order)
// this is obsolete now, because the crew list is stored in the savegame
C4ObjectLink *cLnk;
for (cLnk=Last; cLnk; cLnk=cLnk->Prev)
if (cLnk->Obj->Status)
cLnk->Obj->AssignInfo();
return true;
}
void C4ObjectList::ClearInfo(C4ObjectInfo *pInfo)
{
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
cLnk->Obj->ClearInfo(pInfo);
}
void C4ObjectList::Sort()
{
C4ObjectLink *cLnk;
bool fSorted;
// Sort by id
do
{
fSorted = true;
for (cLnk=First; cLnk && cLnk->Next; cLnk=cLnk->Next)
if (cLnk->Obj->id > cLnk->Next->Obj->id)
{
RemoveLink(cLnk);
InsertLink(cLnk,cLnk->Next);
fSorted = false;
break;
}
}
while (!fSorted);
}
void C4ObjectList::RemoveLink(C4ObjectLink *pLnk)
{
if (pLnk->Prev) pLnk->Prev->Next=pLnk->Next; else First=pLnk->Next;
if (pLnk->Next) pLnk->Next->Prev=pLnk->Prev; else Last=pLnk->Prev;
}
void C4ObjectList::InsertLink(C4ObjectLink *pLnk, C4ObjectLink *pAfter)
{
// Insert after
if (pAfter)
{
pLnk->Prev=pAfter; pLnk->Next=pAfter->Next;
if (pAfter->Next) pAfter->Next->Prev=pLnk; else Last=pLnk;
pAfter->Next=pLnk;
}
// Insert at head
else
{
pLnk->Prev=NULL; pLnk->Next=First;
if (First) First->Prev=pLnk; else Last=pLnk;
First=pLnk;
}
// adjust iterators
if (pAfter)
{
for (iterator* it = FirstIter; it; it = it->Next)
{
if (it->link.Obj == pAfter->Obj)
{
it->link.Next = pLnk;
}
}
}
}
void C4ObjectList::InsertLinkBefore(C4ObjectLink *pLnk, C4ObjectLink *pBefore)
{
// Insert before
if (pBefore)
{
pLnk->Prev = pBefore->Prev;
if (pBefore->Prev) pBefore->Prev->Next = pLnk; else First = pLnk;
pLnk->Next = pBefore; pBefore->Prev = pLnk;
}
// Insert at end
else
{
pLnk->Next = NULL; pLnk->Prev = Last;
if (Last) Last->Next = pLnk; else First = pLnk;
Last = pLnk;
}
// adjust iterators
if (pBefore)
{
for (iterator* it = FirstIter; it; it = it->Next)
{
if (it->link.Obj == pBefore->Obj)
{
it->link.Prev = pLnk;
}
}
}
}
void C4NotifyingObjectList::InsertLinkBefore(C4ObjectLink *pLink, C4ObjectLink *pBefore)
{
C4ObjectList::InsertLinkBefore(pLink, pBefore);
ObjectListChangeListener.OnObjectAdded(this, pLink);
}
void C4NotifyingObjectList::InsertLink(C4ObjectLink *pLink, C4ObjectLink *pAfter)
{
C4ObjectList::InsertLink(pLink, pAfter);
ObjectListChangeListener.OnObjectAdded(this, pLink);
}
void C4NotifyingObjectList::RemoveLink(C4ObjectLink *pLnk)
{
C4ObjectList::RemoveLink(pLnk);
ObjectListChangeListener.OnObjectRemove(this, pLnk);
}
void C4ObjectList::UpdateGraphics(bool fGraphicsChanged)
{
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
cLnk->Obj->UpdateGraphics(fGraphicsChanged);
}
void C4ObjectList::UpdateFaces(bool bUpdateShapes)
{
C4ObjectLink *cLnk;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (cLnk->Obj->Status)
cLnk->Obj->UpdateFace(bUpdateShapes);
}
void C4ObjectList::DrawSelectMark(C4TargetFacet &cgo) const
{
C4ObjectLink *cLnk;
for (cLnk=Last; cLnk; cLnk=cLnk->Prev)
cLnk->Obj->DrawSelectMark(cgo);
}
void C4ObjectList::Copy(const C4ObjectList &rList)
{
Clear(); Default();
C4ObjectLink *cLnk;
for (cLnk=rList.First; cLnk; cLnk=cLnk->Next) Add(cLnk->Obj, C4ObjectList::stNone);
}
void C4ObjectList::Default()
{
First=Last=NULL;
Mass=0;
pEnumerated=NULL;
}
bool C4ObjectList::ShiftContents(C4Object *pNewFirst)
{
// get link of new first (this ensures list is not empty)
C4ObjectLink *pNewFirstLnk = GetLink(pNewFirst);
if (!pNewFirstLnk) return false;
// already at front?
if (pNewFirstLnk == First) return true;
// sort it there:
// 1. Make cyclic list
Last->Next = First; First->Prev = Last;
// 2. Re-set first and last
First = pNewFirstLnk;
Last = pNewFirstLnk->Prev;
// 3. Uncycle list
First->Prev = Last->Next = NULL;
// done, success
return true;
}
void C4ObjectList::DeleteObjects()
{
// delete links and objects
while (First)
{
C4Object *pObj = First->Obj;
if (pObj->Status) Game.ClearPointers(pObj); // clear pointers to removed objects that weren't deleted (game end or section change)
Remove(pObj);
delete pObj;
}
// reset mass
Mass=0;
}
// -------------------------------------------------
// C4ObjectListIterator
C4Object *C4ObjectListIterator::GetNext(int32_t *piCount)
{
// end reached?
if (pCurrID == rList.end()) return NULL;
// not yet started?
if (pCurr == rList.end())
// then start at first ID list head
pCurr = pCurrID;
else
// next item
if (++pCurr == rList.end()) return NULL;
// next ID section reached?
if ((*pCurr)->id != (*pCurrID)->id)
pCurrID = pCurr;
else
{
// otherwise, it must be checked, whether this is a duplicate item already iterated
// if so, advance the list
for (C4ObjectList::iterator pCheck = pCurrID; pCheck != pCurr; ++pCheck)
if ((*pCheck)->CanConcatPictureWith(*pCurr))
{
// next object of matching category
if (++pCurr == rList.end()) return NULL;
// next ID chunk reached?
if ((*pCurr)->id != (*pCurrID)->id)
{
// then break here
pCurrID = pCurr;
break;
}
// restart check for next object
pCheck = pCurrID;
}
}
if (piCount)
{
// default count
*piCount = 1;
// add additional objects of same ID to the count
C4ObjectList::iterator pCheck(pCurr);
for (++pCheck; pCheck != rList.end() && (*pCheck)->id == (*pCurr)->id; ++pCheck)
if ((*pCheck)->CanConcatPictureWith(*pCurr))
++*piCount;
}
// return found object
return *pCurr;
}
void C4ObjectList::UpdateScriptPointers()
{
for (C4ObjectLink *cLnk=First; cLnk; cLnk=cLnk->Next)
cLnk->Obj->UpdateScriptPointers();
}
struct C4ObjectListDumpHelper
{
C4ObjectList *pLst;
C4ValueNumbers * numbers;
void CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(mkParAdapt(*pLst, numbers), "Objects")); }
C4ObjectListDumpHelper(C4ObjectList *pLst, C4ValueNumbers * numbers) : pLst(pLst), numbers(numbers) {}
};
bool C4ObjectList::CheckSort(C4ObjectList *pList)
{
C4ObjectLink *cLnk = First, *cLnk2 = pList->First;
while (cLnk && (!cLnk->Obj->Status || cLnk->Obj->Unsorted)) cLnk = cLnk->Next;
while (cLnk)
if (!cLnk2)
{
Log("CheckSort failure");
C4ValueNumbers numbers;
LogSilent(DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(C4ObjectListDumpHelper(this, &numbers), "SectorList")).getData());
LogSilent(DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(C4ObjectListDumpHelper(pList, &numbers), "MainList")).getData());
return false;
}
else
{
if (cLnk->Obj == cLnk2->Obj)
{
cLnk = cLnk->Next;
while (cLnk && (!cLnk->Obj->Status || cLnk->Obj->Unsorted)) cLnk = cLnk->Next;
}
cLnk2 = cLnk2->Next;
}
return true;
}
void C4ObjectList::CheckCategorySort()
{
// debug: Check whether object list is sorted correctly
C4ObjectLink *cLnk, *cPrev=NULL;
for (cLnk=First; cLnk; cLnk=cLnk->Next)
if (!cLnk->Obj->Unsorted && cLnk->Obj->Status)
{
if (cPrev) assert(cPrev->Obj->GetPlane() >= cLnk->Obj->GetPlane());
cPrev = cLnk;
}
}
C4ObjectList::iterator::iterator(const C4ObjectList & List, const C4ObjectLink * pLink, bool reverse):
List(List), link(pLink ? *pLink : NULL_LINK), reverse(reverse)
{
Next=List.AddIter(this);
}
C4ObjectList::iterator::iterator(const C4ObjectList::iterator & iter):
List(iter.List), link(iter.link), Next(), reverse(iter.reverse)
{
Next=List.AddIter(this);
}
C4ObjectList::iterator::~iterator()
{
List.RemoveIter(this);
}
C4ObjectList::iterator& C4ObjectList::iterator::operator++ ()
{
C4ObjectLink* advance = reverse ? link.Prev : link.Next;
link = advance ? *advance : NULL_LINK;
return *this;
}
C4ObjectList::iterator C4ObjectList::iterator::operator++ (int)
{
iterator old = *this;
iterator::operator++();
return old;
}
C4Object * C4ObjectList::iterator::operator* ()
{
return link.Obj;
}
bool C4ObjectList::iterator::operator== (const iterator & iter) const
{
return
&iter.List == &List &&
iter.link.Obj == link.Obj /* checking for same object should be enough */ &&
iter.reverse == reverse;
}
bool C4ObjectList::iterator::operator!= (const iterator & iter) const
{
return !(*this == iter);
}
bool C4ObjectList::iterator::find(C4Object* target)
{
while (link.Obj)
{
if (link.Obj == target)
{
return true;
}
else
{
(*this)++;
}
}
return false;
}
bool C4ObjectList::iterator::atEnd() const
{
return link.Obj == nullptr;
}
bool C4ObjectList::iterator::reset()
{
C4ObjectLink* l = reverse ? List.Last : List.First;
link = l ? *l : NULL_LINK;
return !atEnd();
}
bool C4ObjectList::iterator::advance()
{
(*this)++;
return !atEnd();
}
C4ObjectList::iterator& C4ObjectList::iterator::operator=(const iterator & iter)
{
// Can only assign iterators into the same list
assert(&iter.List == &List);
link = iter.link;
reverse = iter.reverse;
return *this;
}
C4ObjectList::iterator C4ObjectList::begin() const
{
return iterator(*this, First, false);
}
const C4ObjectList::iterator C4ObjectList::end() const
{
return iterator(*this, nullptr, false);
}
C4ObjectList::iterator * C4ObjectList::AddIter(iterator * iter) const
{
iterator * r = FirstIter;
FirstIter = iter;
return r;
}
void C4ObjectList::RemoveIter(iterator * iter) const
{
if (iter == FirstIter)
FirstIter = iter->Next;
else
{
iterator * i = FirstIter;
while (i->Next && i->Next != iter)
i = i->Next;
i->Next = iter->Next;
}
}
C4ObjectList::iterator C4ObjectList::ReverseView::begin() const
{
return iterator(list, list.Last, true);
}
C4ObjectList::iterator C4ObjectList::ReverseView::end() const
{
return iterator(list, nullptr, true);
}