| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design) |
| 5 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 6 | * |
| 7 | * Distributed under the terms of the ISC license; see accompanying file |
| 8 | * "COPYING" for details. |
| 9 | * |
| 10 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 11 | * See accompanying file "TRADEMARK" for details. |
| 12 | * |
| 13 | * To redistribute this file separately, substitute the full license texts |
| 14 | * for the above references. |
| 15 | */ |
| 16 | |
| 17 | /* Dynamic object list */ |
| 18 | |
| 19 | #include <C4Include.h> |
| 20 | #include <C4ObjectList.h> |
| 21 | |
| 22 | #include <C4Object.h> |
| 23 | #include <C4Wrappers.h> |
| 24 | #include <C4Application.h> |
| 25 | |
| 26 | #include <format> |
| 27 | |
| 28 | C4ObjectList::C4ObjectList() : FirstIter(nullptr) |
| 29 | { |
| 30 | Default(); |
| 31 | } |
| 32 | |
| 33 | C4ObjectList::C4ObjectList(const C4ObjectList &List) : FirstIter(nullptr) |
| 34 | { |
| 35 | Default(); |
| 36 | Copy(rList: List); |
| 37 | } |
| 38 | |
| 39 | C4ObjectList::~C4ObjectList() |
| 40 | { |
| 41 | Clear(); |
| 42 | } |
| 43 | |
| 44 | void C4ObjectList::Clear() |
| 45 | { |
| 46 | C4ObjectLink *cLnk, *nextLnk; |
| 47 | for (cLnk = First; cLnk; cLnk = nextLnk) |
| 48 | { |
| 49 | nextLnk = cLnk->Next; delete cLnk; |
| 50 | } |
| 51 | First = Last = nullptr; |
| 52 | pEnumerated.reset(); |
| 53 | } |
| 54 | |
| 55 | const int MaxTempListID = 500; |
| 56 | C4ID TempListID[MaxTempListID]; |
| 57 | |
| 58 | C4ID C4ObjectList::GetListID(int32_t dwCategory, int Index) |
| 59 | { |
| 60 | int clid; |
| 61 | C4ObjectLink *clnk; |
| 62 | C4Def *cdef; |
| 63 | |
| 64 | // Create a temporary list of all id's and counts |
| 65 | for (clid = 0; clid < MaxTempListID; clid++) TempListID[clid] = C4ID_None; |
| 66 | for (clnk = First; clnk && clnk->Obj; clnk = clnk->Next) |
| 67 | if (clnk->Obj->Status) |
| 68 | if ((dwCategory == C4D_All) || ((cdef = C4Id2Def(id: clnk->Obj->Def->id)) && (cdef->Category & dwCategory))) |
| 69 | for (clid = 0; clid < MaxTempListID; clid++) |
| 70 | { |
| 71 | // Already there |
| 72 | if (TempListID[clid] == clnk->Obj->Def->id) break; |
| 73 | // End of list, add id |
| 74 | if (TempListID[clid] == C4ID_None) { TempListID[clid] = clnk->Obj->Def->id; break; } |
| 75 | } |
| 76 | |
| 77 | // Returns indexed id |
| 78 | if (Inside(ival: Index, lbound: 0, rbound: MaxTempListID - 1)) return TempListID[Index]; |
| 79 | |
| 80 | return C4ID_None; |
| 81 | } |
| 82 | |
| 83 | int C4ObjectList::ListIDCount(int32_t dwCategory) |
| 84 | { |
| 85 | int clid; |
| 86 | C4ObjectLink *clnk; |
| 87 | C4Def *cdef; |
| 88 | |
| 89 | // Create a temporary list of all id's and counts |
| 90 | for (clid = 0; clid < MaxTempListID; clid++) TempListID[clid] = C4ID_None; |
| 91 | for (clnk = First; clnk && clnk->Obj; clnk = clnk->Next) |
| 92 | if (clnk->Obj->Status) |
| 93 | if ((dwCategory == C4D_All) || ((cdef = C4Id2Def(id: clnk->Obj->Def->id)) && (cdef->Category & dwCategory))) |
| 94 | for (clid = 0; clid < MaxTempListID; clid++) |
| 95 | { |
| 96 | // Already there |
| 97 | if (TempListID[clid] == clnk->Obj->Def->id) break; |
| 98 | // End of list, add id |
| 99 | if (TempListID[clid] == C4ID_None) { TempListID[clid] = clnk->Obj->Def->id; break; } |
| 100 | } |
| 101 | |
| 102 | // Count different id's |
| 103 | for (clid = 0; clid < MaxTempListID; clid++) |
| 104 | if (TempListID[clid] == C4ID_None) |
| 105 | return clid; |
| 106 | |
| 107 | return MaxTempListID; |
| 108 | } |
| 109 | |
| 110 | bool C4ObjectList::Add(C4Object *nObj, SortType eSort, C4ObjectList *pLstSorted) |
| 111 | { |
| 112 | if (!nObj || !nObj->Def || !nObj->Status) return false; |
| 113 | |
| 114 | #ifndef NDEBUG |
| 115 | if (eSort == stMain) |
| 116 | { |
| 117 | CheckCategorySort(); |
| 118 | if (pLstSorted) |
| 119 | assert(CheckSort(pLstSorted)); |
| 120 | } |
| 121 | #endif |
| 122 | |
| 123 | // dbg: don't do double links |
| 124 | assert(!GetLink(nObj)); |
| 125 | |
| 126 | // no self-sort |
| 127 | assert(pLstSorted != this); |
| 128 | |
| 129 | // Allocate new link |
| 130 | auto newLink = std::make_unique<C4ObjectLink>(); |
| 131 | // Set link |
| 132 | newLink->Obj = nObj; |
| 133 | |
| 134 | // Search insert position (default: end of list) |
| 135 | C4ObjectLink *cLnk = nullptr, *cPrev = Last; |
| 136 | |
| 137 | // Should sort? |
| 138 | if (eSort == stReverse) |
| 139 | { |
| 140 | // reverse sort: Add to beginning of list |
| 141 | cLnk = First; cPrev = nullptr; |
| 142 | } |
| 143 | else if (eSort) |
| 144 | { |
| 145 | cLnk = nullptr; cPrev = Last; |
| 146 | |
| 147 | // Sort override or line? Leave default as is. |
| 148 | bool fUnsorted = nObj->Unsorted || nObj->Def->Line; |
| 149 | if (!fUnsorted) |
| 150 | { |
| 151 | // Find successor by matching category / id |
| 152 | // Sort by matching category/id is necessary for inventory shifting. |
| 153 | // It is not done for static back to allow multiobject outside structure. |
| 154 | // Unsorted objects are ignored in comparison. |
| 155 | if (!(nObj->Category & C4D_StaticBack)) |
| 156 | for (cPrev = nullptr, cLnk = First; cLnk; cLnk = cLnk->Next) |
| 157 | if (cLnk->Obj->Status && !cLnk->Obj->Unsorted) |
| 158 | { |
| 159 | if ((cLnk->Obj->Category & C4D_SortLimit) == (nObj->Category & C4D_SortLimit)) |
| 160 | if (cLnk->Obj->id == nObj->id) |
| 161 | break; |
| 162 | cPrev = cLnk; |
| 163 | } |
| 164 | |
| 165 | // Find successor by relative category |
| 166 | if (!cLnk) |
| 167 | for (cPrev = nullptr, cLnk = First; cLnk; cLnk = cLnk->Next) |
| 168 | if (cLnk->Obj->Status && !cLnk->Obj->Unsorted) |
| 169 | { |
| 170 | if ((cLnk->Obj->Category & C4D_SortLimit) <= (nObj->Category & C4D_SortLimit)) |
| 171 | break; |
| 172 | cPrev = cLnk; |
| 173 | } |
| 174 | |
| 175 | cLnk = cPrev ? cPrev->Next : First; |
| 176 | } |
| 177 | |
| 178 | // Sort by master list? |
| 179 | if (pLstSorted) |
| 180 | { |
| 181 | assert(CheckSort(pLstSorted)); |
| 182 | |
| 183 | // Unsorted: Always search full list (start with first object in list) |
| 184 | if (fUnsorted) { cLnk = First; cPrev = nullptr; } |
| 185 | |
| 186 | // As cPrev is the last link in front of the first position where the object could be inserted, |
| 187 | // the object should be after this point in the master list (given it's consistent). |
| 188 | // If we're about to insert the object at the end of the list, there is obviously nothing to do. |
| 189 | #ifdef NDEBUG |
| 190 | if (cLnk) |
| 191 | { |
| 192 | #endif |
| 193 | C4ObjectLink *cLnk2 = cPrev ? pLstSorted->GetLink(pObj: cPrev->Obj)->Next : pLstSorted->First; |
| 194 | for (; cLnk2; cLnk2 = cLnk2->Next) |
| 195 | if (cLnk2->Obj == nObj) |
| 196 | // Position found! |
| 197 | break; |
| 198 | else if (cLnk && cLnk2->Obj == cLnk->Obj) |
| 199 | { |
| 200 | // So cLnk->Obj is actually in front of nObj. Update insert position |
| 201 | cPrev = cLnk; |
| 202 | cLnk = cLnk->Next; |
| 203 | #ifdef NDEBUG |
| 204 | // At end of list? |
| 205 | if (!cLnk) break; |
| 206 | #endif |
| 207 | } |
| 208 | |
| 209 | // No position found? This shouldn't happen with a consistent main list. |
| 210 | assert(cLnk2); |
| 211 | #ifdef NDEBUG |
| 212 | } |
| 213 | #endif |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | assert(!cPrev || cPrev->Next == cLnk); |
| 218 | assert(!cLnk || cLnk->Prev == cPrev); |
| 219 | |
| 220 | // Insert new link after predecessor |
| 221 | InsertLink(pLink: newLink.get(), pAfter: cPrev); |
| 222 | newLink.release(); |
| 223 | |
| 224 | #ifndef NDEBUG |
| 225 | // Debug: Check sort |
| 226 | if (eSort == stMain) |
| 227 | { |
| 228 | CheckCategorySort(); |
| 229 | if (pLstSorted) |
| 230 | assert(CheckSort(pLstSorted)); |
| 231 | } |
| 232 | #endif |
| 233 | |
| 234 | // Add mass |
| 235 | Mass += nObj->Mass; |
| 236 | |
| 237 | return true; |
| 238 | } |
| 239 | |
| 240 | bool C4ObjectList::Remove(C4Object *pObj) |
| 241 | { |
| 242 | C4ObjectLink *cLnk; |
| 243 | |
| 244 | // Find link |
| 245 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 246 | if (cLnk->Obj == pObj) break; |
| 247 | if (!cLnk) return false; |
| 248 | |
| 249 | // Fix iterators |
| 250 | for (iterator *i = FirstIter; i; i = i->Next) |
| 251 | { |
| 252 | if (i->pLink == cLnk) i->pLink = cLnk->*(i->direction); |
| 253 | } |
| 254 | |
| 255 | // Remove link from list |
| 256 | RemoveLink(pLnk: cLnk); |
| 257 | |
| 258 | // Deallocate link |
| 259 | delete cLnk; |
| 260 | |
| 261 | // Remove mass |
| 262 | Mass -= pObj->Mass; if (Mass < 0) Mass = 0; |
| 263 | |
| 264 | #ifndef NDEBUG |
| 265 | if (GetLink(pObj)) BREAKPOINT_HERE; |
| 266 | #endif |
| 267 | |
| 268 | return true; |
| 269 | } |
| 270 | |
| 271 | C4Object *C4ObjectList::Find(C4ID id, int owner, uint32_t dwOCF) |
| 272 | { |
| 273 | C4ObjectLink *cLnk; |
| 274 | // Find link and object |
| 275 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 276 | if (cLnk->Obj->Status) |
| 277 | if (cLnk->Obj->Def->id == id) |
| 278 | if ((owner == ANY_OWNER) || (cLnk->Obj->Owner == owner)) |
| 279 | if (dwOCF & cLnk->Obj->OCF) |
| 280 | return cLnk->Obj; |
| 281 | return nullptr; |
| 282 | } |
| 283 | |
| 284 | C4Object *C4ObjectList::FindOther(C4ID id, int owner) |
| 285 | { |
| 286 | C4ObjectLink *cLnk; |
| 287 | // Find link and object |
| 288 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 289 | if (cLnk->Obj->Status) |
| 290 | if (cLnk->Obj->Def->id != id) |
| 291 | if ((owner == ANY_OWNER) || (cLnk->Obj->Owner == owner)) |
| 292 | return cLnk->Obj; |
| 293 | return nullptr; |
| 294 | } |
| 295 | |
| 296 | C4Object *C4ObjectList::GetObject(int Index) |
| 297 | { |
| 298 | int cIdx; |
| 299 | C4ObjectLink *cLnk; |
| 300 | // Find link and object |
| 301 | for (cLnk = First, cIdx = 0; cLnk; cLnk = cLnk->Next) |
| 302 | if (cLnk->Obj->Status) |
| 303 | { |
| 304 | if (cIdx == Index) return cLnk->Obj; |
| 305 | cIdx++; |
| 306 | } |
| 307 | return nullptr; |
| 308 | } |
| 309 | |
| 310 | C4ObjectLink *C4ObjectList::GetLink(C4Object *pObj) |
| 311 | { |
| 312 | if (!pObj) return nullptr; |
| 313 | C4ObjectLink *cLnk; |
| 314 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 315 | if (cLnk->Obj == pObj) |
| 316 | return cLnk; |
| 317 | return nullptr; |
| 318 | } |
| 319 | |
| 320 | int C4ObjectList::ObjectCount(C4ID id, int32_t dwCategory) const |
| 321 | { |
| 322 | C4ObjectLink *cLnk; |
| 323 | int iCount = 0; |
| 324 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 325 | if (cLnk->Obj->Status) |
| 326 | if ((id == C4ID_None) || (cLnk->Obj->Def->id == id)) |
| 327 | if ((dwCategory == C4D_All) || (cLnk->Obj->Category & dwCategory)) |
| 328 | iCount++; |
| 329 | return iCount; |
| 330 | } |
| 331 | |
| 332 | int C4ObjectList::MassCount() |
| 333 | { |
| 334 | C4ObjectLink *cLnk; |
| 335 | int iMass = 0; |
| 336 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 337 | if (cLnk->Obj->Status) |
| 338 | iMass += cLnk->Obj->Mass; |
| 339 | Mass = iMass; |
| 340 | return iMass; |
| 341 | } |
| 342 | |
| 343 | void C4ObjectList::DrawIDList(C4Facet &cgo, int iSelection, |
| 344 | C4DefList &rDefs, int32_t dwCategory, |
| 345 | C4RegionList *pRegions, int iRegionCom, |
| 346 | bool fDrawOneCounts) |
| 347 | { |
| 348 | // Variables |
| 349 | int32_t cSec = 0; |
| 350 | int32_t iCount; |
| 351 | C4Facet cgo2; |
| 352 | C4Object *pFirstObj; |
| 353 | std::array<char, C4Strings::NumberOfCharactersForDigits<std::int32_t> + 1 + 1> buf; |
| 354 | // objects are sorted in the list already, so just draw them! |
| 355 | C4ObjectListIterator iter(*this); |
| 356 | while (pFirstObj = iter.GetNext(piCount: &iCount)) |
| 357 | { |
| 358 | // Section |
| 359 | cgo2 = cgo.GetSection(iSection: cSec); |
| 360 | // draw picture |
| 361 | pFirstObj->DrawPicture(cgo&: cgo2, fSelected: cSec == iSelection); |
| 362 | // Draw count |
| 363 | char *const ptr{std::to_chars(first: buf.data(), last: buf.data() + buf.size() - 2, value: iCount).ptr}; |
| 364 | ptr[0] = 'x'; |
| 365 | ptr[1] = '\0'; |
| 366 | if ((iCount != 1) || fDrawOneCounts) |
| 367 | Application.DDraw->TextOut(szText: buf.data(), rFont&: Game.GraphicsResource.FontRegular, fZoom: 1.0, sfcDest: cgo2.Surface, iTx: cgo2.X + cgo2.Wdt - 1, iTy: cgo2.Y + cgo2.Hgt - 1 - Game.GraphicsResource.FontRegular.GetLineHeight(), dwFCol: CStdDDraw::DEFAULT_MESSAGE_COLOR, byForm: ARight); |
| 368 | // Region |
| 369 | if (pRegions) pRegions->Add(iX: cgo2.X, iY: cgo2.Y, iWdt: cgo2.Wdt, iHgt: cgo2.Hgt, szCaption: pFirstObj->GetName(), iCom: iRegionCom, pTarget: pFirstObj, iMoveOverCom: COM_None, iHoldCom: COM_None, iData: pFirstObj->Number); |
| 370 | // Next section |
| 371 | cSec++; |
| 372 | } |
| 373 | } |
| 374 | |
| 375 | int C4ObjectList::ClearPointers(C4Object *pObj) |
| 376 | { |
| 377 | int rval = 0; |
| 378 | // Clear all primary list pointers |
| 379 | while (Remove(pObj)) rval++; |
| 380 | // Clear all sub pointers |
| 381 | C4Object *cobj; C4ObjectLink *clnk; |
| 382 | for (clnk = First; clnk && (cobj = clnk->Obj); clnk = clnk->Next) |
| 383 | cobj->ClearPointers(ptr: pObj); |
| 384 | return rval; |
| 385 | } |
| 386 | |
| 387 | void C4ObjectList::DrawAll(C4FacetEx &cgo, int iPlayer) |
| 388 | { |
| 389 | C4ObjectLink *clnk; |
| 390 | // Draw objects (base) |
| 391 | for (clnk = Last; clnk; clnk = clnk->Prev) |
| 392 | clnk->Obj->Draw(cgo, iByPlayer: iPlayer); |
| 393 | // Draw objects (top face) |
| 394 | for (clnk = Last; clnk; clnk = clnk->Prev) |
| 395 | clnk->Obj->DrawTopFace(cgo, iByPlayer: iPlayer); |
| 396 | } |
| 397 | |
| 398 | void C4ObjectList::DrawIfCategory(C4FacetEx &cgo, int iPlayer, uint32_t dwCat, bool fInvert) |
| 399 | { |
| 400 | C4ObjectLink *clnk; |
| 401 | // Draw objects (base) |
| 402 | for (clnk = Last; clnk; clnk = clnk->Prev) |
| 403 | if (!(clnk->Obj->Category & dwCat) == fInvert) |
| 404 | clnk->Obj->Draw(cgo, iByPlayer: iPlayer); |
| 405 | // Draw objects (top face) |
| 406 | for (clnk = Last; clnk; clnk = clnk->Prev) |
| 407 | if (!(clnk->Obj->Category & dwCat) == fInvert) |
| 408 | clnk->Obj->DrawTopFace(cgo, iByPlayer: iPlayer); |
| 409 | } |
| 410 | |
| 411 | void C4ObjectList::Draw(C4FacetEx &cgo, int iPlayer) |
| 412 | { |
| 413 | C4ObjectLink *clnk; |
| 414 | // Draw objects (base) |
| 415 | for (clnk = Last; clnk; clnk = clnk->Prev) |
| 416 | if (!(clnk->Obj->Category & C4D_BackgroundOrForeground)) |
| 417 | clnk->Obj->Draw(cgo, iByPlayer: iPlayer); |
| 418 | // Draw objects (top face) |
| 419 | for (clnk = Last; clnk; clnk = clnk->Prev) |
| 420 | if (!(clnk->Obj->Category & C4D_BackgroundOrForeground)) |
| 421 | clnk->Obj->DrawTopFace(cgo, iByPlayer: iPlayer); |
| 422 | } |
| 423 | |
| 424 | void C4ObjectList::Enumerate() |
| 425 | { |
| 426 | C4ObjectLink *cLnk; |
| 427 | // Enumerate object pointers |
| 428 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 429 | if (cLnk->Obj->Status) |
| 430 | cLnk->Obj->EnumeratePointers(); |
| 431 | } |
| 432 | |
| 433 | int32_t C4ObjectList::ObjectNumber(C4Object *pObj) |
| 434 | { |
| 435 | C4ObjectLink *cLnk; |
| 436 | if (!pObj) return 0; |
| 437 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 438 | if (cLnk->Obj == pObj) |
| 439 | return cLnk->Obj->Number; |
| 440 | return 0; |
| 441 | } |
| 442 | |
| 443 | bool C4ObjectList::IsContained(C4Object *pObj) |
| 444 | { |
| 445 | C4ObjectLink *cLnk; |
| 446 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 447 | if (cLnk->Obj == pObj) |
| 448 | return true; |
| 449 | return false; |
| 450 | } |
| 451 | |
| 452 | bool C4ObjectList::IsClear() const |
| 453 | { |
| 454 | return (ObjectCount() == 0); |
| 455 | } |
| 456 | |
| 457 | bool C4ObjectList::DenumerateRead() |
| 458 | { |
| 459 | if (!pEnumerated) return false; |
| 460 | // Denumerate all object pointers |
| 461 | for (const auto num : *pEnumerated) |
| 462 | Add(nObj: Game.Objects.ObjectPointer(iNumber: num), eSort: stNone); // Add to tail, unsorted |
| 463 | // Delete old list |
| 464 | pEnumerated.reset(); |
| 465 | return true; |
| 466 | } |
| 467 | |
| 468 | void C4ObjectList::Denumerate() |
| 469 | { |
| 470 | C4ObjectLink *cLnk; |
| 471 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 472 | if (cLnk->Obj->Status) |
| 473 | cLnk->Obj->DenumeratePointers(); |
| 474 | } |
| 475 | |
| 476 | void C4ObjectList::CompileFunc(StdCompiler *pComp, bool fSaveRefs, bool fSkipPlayerObjects) |
| 477 | { |
| 478 | if (fSaveRefs) |
| 479 | { |
| 480 | // this mode not supported |
| 481 | assert(!fSkipPlayerObjects); |
| 482 | // (Re)create list |
| 483 | pEnumerated.reset(p: new decltype(pEnumerated)::element_type); |
| 484 | // Decompiling: Build list |
| 485 | if (!pComp->isCompiler()) |
| 486 | for (C4ObjectLink *pPos = First; pPos; pPos = pPos->Next) |
| 487 | if (pPos->Obj->Status) |
| 488 | pEnumerated->push_back(x: pPos->Obj->Number); |
| 489 | // Compile list |
| 490 | pComp->Value(rStruct: mkSTLContainerAdapt(rTarget&: *pEnumerated, eSep: StdCompiler::SEP_SEP2)); |
| 491 | // Decompiling: Delete list |
| 492 | if (!pComp->isCompiler()) |
| 493 | { |
| 494 | pEnumerated.reset(); |
| 495 | } |
| 496 | // Compiling: Nothing to do - list will e denumerated later |
| 497 | } |
| 498 | else |
| 499 | { |
| 500 | if (pComp->isDecompiler()) |
| 501 | { |
| 502 | // skipping player objects would screw object counting in non-naming compilers |
| 503 | assert(!fSkipPlayerObjects || pComp->hasNaming()); |
| 504 | // Put object count |
| 505 | int32_t iObjCnt = ObjectCount(); |
| 506 | pComp->Value(rStruct: mkNamingCountAdapt(iCount&: iObjCnt, szName: "Object" )); |
| 507 | // Decompile all objects in reverse order |
| 508 | for (C4ObjectLink *pPos = Last; pPos; pPos = pPos->Prev) |
| 509 | if (pPos->Obj->Status) |
| 510 | if (!fSkipPlayerObjects || !pPos->Obj->IsUserPlayerObject()) |
| 511 | pComp->Value(rStruct: mkNamingAdapt(rValue&: *pPos->Obj, szName: "Object" )); |
| 512 | } |
| 513 | else |
| 514 | { |
| 515 | // this mode not supported |
| 516 | assert(!fSkipPlayerObjects); |
| 517 | // Remove previous data |
| 518 | Clear(); |
| 519 | // Get "Object" section count |
| 520 | int32_t iObjCnt; |
| 521 | pComp->Value(rStruct: mkNamingCountAdapt(iCount&: iObjCnt, szName: "Object" )); |
| 522 | // Load objects, add them to the list. |
| 523 | for (int i = 0; i < iObjCnt; i++) |
| 524 | { |
| 525 | C4Object *pObj = nullptr; |
| 526 | try |
| 527 | { |
| 528 | pComp->Value(rStruct: mkNamingAdapt(rValue: mkPtrAdaptNoNull(rpObj&: pObj), szName: "Object" )); |
| 529 | Add(nObj: pObj, eSort: stReverse); |
| 530 | } |
| 531 | catch (const StdCompiler::Exception &e) |
| 532 | { |
| 533 | // Failsafe object loading: If an error occurs during object loading, just skip that object and load the next one |
| 534 | if (e.Pos.empty()) |
| 535 | LogNTr(level: spdlog::level::err, fmt: "Object loading: {}" , args: e.what()); |
| 536 | else |
| 537 | LogNTr(level: spdlog::level::err, fmt: "Object loading({}): {}" , args: e.Pos, args: e.what()); |
| 538 | } |
| 539 | } |
| 540 | } |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | C4Object *C4ObjectList::ObjectPointer(int32_t iNumber) |
| 545 | { |
| 546 | C4ObjectLink *cLnk; |
| 547 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 548 | if (cLnk->Obj->Number == iNumber) |
| 549 | return cLnk->Obj; |
| 550 | return nullptr; |
| 551 | } |
| 552 | |
| 553 | C4Object *C4ObjectList::SafeObjectPointer(int32_t iNumber) |
| 554 | { |
| 555 | C4Object *pObj = ObjectPointer(iNumber); |
| 556 | if (pObj) if (!pObj->Status) return nullptr; |
| 557 | return pObj; |
| 558 | } |
| 559 | |
| 560 | std::string C4ObjectList::GetNameList(C4DefList &rDefs, uint32_t dwCategory) |
| 561 | { |
| 562 | int cpos, idcount; |
| 563 | C4ID c_id; |
| 564 | C4Def *cdef; |
| 565 | std::string result; |
| 566 | for (cpos = 0; c_id = GetListID(dwCategory, Index: cpos); cpos++) |
| 567 | if (cdef = rDefs.ID2Def(id: c_id)) |
| 568 | { |
| 569 | idcount = ObjectCount(id: c_id); |
| 570 | if (cpos > 0) result += ", " ; |
| 571 | result += std::format(fmt: "{}x {}" , args&: idcount, args: cdef->GetName()); |
| 572 | } |
| 573 | return result; |
| 574 | } |
| 575 | |
| 576 | bool C4ObjectList::ValidateOwners() |
| 577 | { |
| 578 | C4ObjectLink *cLnk; |
| 579 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 580 | if (cLnk->Obj->Status) |
| 581 | cLnk->Obj->ValidateOwner(); |
| 582 | return true; |
| 583 | } |
| 584 | |
| 585 | bool C4ObjectList::AssignInfo() |
| 586 | { |
| 587 | // the list seems to be traced backwards here, to ensure crew objects are added in correct order |
| 588 | // (or semi-correct, because this will work only if the crew order matches the main object list order) |
| 589 | // this is obsolete now, because the crew list is stored in the savegame |
| 590 | C4ObjectLink *cLnk; |
| 591 | for (cLnk = Last; cLnk; cLnk = cLnk->Prev) |
| 592 | if (cLnk->Obj->Status) |
| 593 | cLnk->Obj->AssignInfo(); |
| 594 | return true; |
| 595 | } |
| 596 | |
| 597 | bool C4ObjectList::AssignPlrViewRange() |
| 598 | { |
| 599 | C4ObjectLink *cLnk; |
| 600 | for (cLnk = Last; cLnk; cLnk = cLnk->Prev) |
| 601 | if (cLnk->Obj->Status) |
| 602 | cLnk->Obj->AssignPlrViewRange(); |
| 603 | return true; |
| 604 | } |
| 605 | |
| 606 | void C4ObjectList::ClearInfo(C4ObjectInfo *pInfo) |
| 607 | { |
| 608 | C4ObjectLink *cLnk; |
| 609 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 610 | if (cLnk->Obj->Status) |
| 611 | cLnk->Obj->ClearInfo(pInfo); |
| 612 | } |
| 613 | |
| 614 | void C4ObjectList::RemoveLink(C4ObjectLink *pLnk) |
| 615 | { |
| 616 | if (pLnk->Prev) pLnk->Prev->Next = pLnk->Next; else First = pLnk->Next; |
| 617 | if (pLnk->Next) pLnk->Next->Prev = pLnk->Prev; else Last = pLnk->Prev; |
| 618 | } |
| 619 | |
| 620 | void C4ObjectList::InsertLink(C4ObjectLink *pLnk, C4ObjectLink *pAfter) |
| 621 | { |
| 622 | // Insert after |
| 623 | if (pAfter) |
| 624 | { |
| 625 | pLnk->Prev = pAfter; pLnk->Next = pAfter->Next; |
| 626 | if (pAfter->Next) pAfter->Next->Prev = pLnk; else Last = pLnk; |
| 627 | pAfter->Next = pLnk; |
| 628 | } |
| 629 | // Insert at head |
| 630 | else |
| 631 | { |
| 632 | pLnk->Prev = nullptr; pLnk->Next = First; |
| 633 | if (First) First->Prev = pLnk; else Last = pLnk; |
| 634 | First = pLnk; |
| 635 | } |
| 636 | } |
| 637 | |
| 638 | void C4ObjectList::InsertLinkBefore(C4ObjectLink *pLnk, C4ObjectLink *pBefore) |
| 639 | { |
| 640 | // Insert before |
| 641 | if (pBefore) |
| 642 | { |
| 643 | pLnk->Prev = pBefore->Prev; |
| 644 | if (pBefore->Prev) pBefore->Prev->Next = pLnk; else First = pLnk; |
| 645 | pLnk->Next = pBefore; pBefore->Prev = pLnk; |
| 646 | } |
| 647 | // Insert at end |
| 648 | else |
| 649 | { |
| 650 | pLnk->Next = nullptr; pLnk->Prev = Last; |
| 651 | if (Last) Last->Next = pLnk; else First = pLnk; |
| 652 | Last = pLnk; |
| 653 | } |
| 654 | } |
| 655 | |
| 656 | void C4NotifyingObjectList::InsertLinkBefore(C4ObjectLink *pLink, C4ObjectLink *pBefore) |
| 657 | { |
| 658 | C4ObjectList::InsertLinkBefore(pLnk: pLink, pBefore); |
| 659 | ObjectListChangeListener.OnObjectAdded(pList: this, pLnk: pLink); |
| 660 | } |
| 661 | |
| 662 | void C4NotifyingObjectList::InsertLink(C4ObjectLink *pLink, C4ObjectLink *pAfter) |
| 663 | { |
| 664 | C4ObjectList::InsertLink(pLnk: pLink, pAfter); |
| 665 | ObjectListChangeListener.OnObjectAdded(pList: this, pLnk: pLink); |
| 666 | } |
| 667 | |
| 668 | void C4NotifyingObjectList::RemoveLink(C4ObjectLink *pLnk) |
| 669 | { |
| 670 | C4ObjectList::RemoveLink(pLnk); |
| 671 | ObjectListChangeListener.OnObjectRemove(pList: this, pLnk); |
| 672 | } |
| 673 | |
| 674 | void C4ObjectList::SyncClearance() |
| 675 | { |
| 676 | C4ObjectLink *cLnk; |
| 677 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 678 | if (cLnk->Obj) |
| 679 | cLnk->Obj->SyncClearance(); |
| 680 | } |
| 681 | |
| 682 | void C4ObjectList::UpdateGraphics(bool fGraphicsChanged) |
| 683 | { |
| 684 | C4ObjectLink *cLnk; |
| 685 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 686 | if (cLnk->Obj->Status) |
| 687 | cLnk->Obj->UpdateGraphics(fGraphicsChanged); |
| 688 | } |
| 689 | |
| 690 | void C4ObjectList::UpdateFaces(bool bUpdateShapes) |
| 691 | { |
| 692 | C4ObjectLink *cLnk; |
| 693 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 694 | if (cLnk->Obj->Status) |
| 695 | cLnk->Obj->UpdateFace(bUpdateShape: bUpdateShapes); |
| 696 | } |
| 697 | |
| 698 | void C4ObjectList::DrawSelectMark(C4FacetEx &cgo) |
| 699 | { |
| 700 | C4ObjectLink *cLnk; |
| 701 | for (cLnk = Last; cLnk; cLnk = cLnk->Prev) |
| 702 | cLnk->Obj->DrawSelectMark(cgo); |
| 703 | } |
| 704 | |
| 705 | void C4ObjectList::() |
| 706 | { |
| 707 | C4Object *cobj; C4ObjectLink *clnk; |
| 708 | for (clnk = First; clnk && (cobj = clnk->Obj); clnk = clnk->Next) |
| 709 | cobj->CloseMenu(fForce: true); |
| 710 | } |
| 711 | |
| 712 | void C4ObjectList::SetOCF() |
| 713 | { |
| 714 | C4ObjectLink *cLnk; |
| 715 | for (cLnk = First; cLnk; cLnk = cLnk->Next) |
| 716 | if (cLnk->Obj->Status) |
| 717 | cLnk->Obj->SetOCF(); |
| 718 | } |
| 719 | |
| 720 | void C4ObjectList::Copy(const C4ObjectList &rList) |
| 721 | { |
| 722 | Clear(); Default(); |
| 723 | C4ObjectLink *cLnk; |
| 724 | for (cLnk = rList.First; cLnk; cLnk = cLnk->Next) Add(nObj: cLnk->Obj, eSort: C4ObjectList::stNone); |
| 725 | } |
| 726 | |
| 727 | void C4ObjectList::Default() |
| 728 | { |
| 729 | First = Last = nullptr; |
| 730 | Mass = 0; |
| 731 | pEnumerated.reset(); |
| 732 | } |
| 733 | |
| 734 | void C4ObjectList::UpdateTransferZones() |
| 735 | { |
| 736 | C4Object *cobj; C4ObjectLink *clnk; |
| 737 | for (clnk = First; clnk && (cobj = clnk->Obj); clnk = clnk->Next) |
| 738 | cobj->Call(PSF_UpdateTransferZone); |
| 739 | } |
| 740 | |
| 741 | void C4ObjectList::ResetAudibility() |
| 742 | { |
| 743 | C4Object *cobj; C4ObjectLink *clnk; |
| 744 | for (clnk = First; clnk && (cobj = clnk->Obj); clnk = clnk->Next) |
| 745 | { |
| 746 | cobj->ResetAudibility(); |
| 747 | } |
| 748 | } |
| 749 | |
| 750 | void C4ObjectList::SortByCategory() |
| 751 | { |
| 752 | C4ObjectLink *cLnk; |
| 753 | bool fSorted; |
| 754 | // Sort by category |
| 755 | do |
| 756 | { |
| 757 | fSorted = true; |
| 758 | for (cLnk = First; cLnk && cLnk->Next; cLnk = cLnk->Next) |
| 759 | if ((cLnk->Obj->Category & C4D_SortLimit) < (cLnk->Next->Obj->Category & C4D_SortLimit)) |
| 760 | { |
| 761 | RemoveLink(pLnk: cLnk); |
| 762 | InsertLink(pLnk: cLnk, pAfter: cLnk->Next); |
| 763 | fSorted = false; |
| 764 | break; |
| 765 | } |
| 766 | } while (!fSorted); |
| 767 | } |
| 768 | |
| 769 | bool C4ObjectList::OrderObjectBefore(C4Object *pObj1, C4Object *pObj2) |
| 770 | { |
| 771 | // safety |
| 772 | if (pObj1->Status != C4OS_NORMAL || pObj2->Status != C4OS_NORMAL) return false; |
| 773 | // get links (and check whether the objects are part of this list!) |
| 774 | C4ObjectLink *pLnk1 = GetLink(pObj: pObj1); if (!pLnk1) return false; |
| 775 | C4ObjectLink *pLnk2 = GetLink(pObj: pObj2); if (!pLnk2) return false; |
| 776 | // check if requirements are already fulfilled |
| 777 | C4ObjectLink *pLnk = pLnk1; |
| 778 | while (pLnk = pLnk->Next) if (pLnk == pLnk2) break; |
| 779 | if (pLnk) return true; |
| 780 | // if not, reorder pLnk1 directly before pLnk2 |
| 781 | // unlink from current position |
| 782 | // no need to check pLnk1->Prev here, because pLnk1 cannot be first in the list |
| 783 | // (at least pLnk2 must lie before it!) |
| 784 | if (pLnk1->Prev->Next = pLnk1->Next) pLnk1->Next->Prev = pLnk1->Prev; else Last = pLnk1->Prev; |
| 785 | // relink into new one |
| 786 | if (pLnk1->Prev = pLnk2->Prev) pLnk2->Prev->Next = pLnk1; else First = pLnk1; |
| 787 | pLnk1->Next = pLnk2; pLnk2->Prev = pLnk1; |
| 788 | // done, success |
| 789 | return true; |
| 790 | } |
| 791 | |
| 792 | bool C4ObjectList::OrderObjectAfter(C4Object *pObj1, C4Object *pObj2) |
| 793 | { |
| 794 | // safety |
| 795 | if (pObj1->Status != C4OS_NORMAL || pObj2->Status != C4OS_NORMAL) return false; |
| 796 | // get links (and check whether the objects are part of this list!) |
| 797 | C4ObjectLink *pLnk1 = GetLink(pObj: pObj1); if (!pLnk1) return false; |
| 798 | C4ObjectLink *pLnk2 = GetLink(pObj: pObj2); if (!pLnk2) return false; |
| 799 | // check if requirements are already fulfilled |
| 800 | C4ObjectLink *pLnk = pLnk1; |
| 801 | while (pLnk = pLnk->Prev) if (pLnk == pLnk2) break; |
| 802 | if (pLnk) return true; |
| 803 | // if not, reorder pLnk1 directly after pLnk2 |
| 804 | // unlink from current position |
| 805 | // no need to check pLnk1->Next here, because pLnk1 cannot be last in the list |
| 806 | // (at least pLnk2 must lie after it!) |
| 807 | if (pLnk1->Next->Prev = pLnk1->Prev) pLnk1->Prev->Next = pLnk1->Next; else First = pLnk1->Next; |
| 808 | // relink into new one |
| 809 | if (pLnk1->Next = pLnk2->Next) pLnk2->Next->Prev = pLnk1; else Last = pLnk1; |
| 810 | pLnk1->Prev = pLnk2; pLnk2->Next = pLnk1; |
| 811 | // done, success |
| 812 | return true; |
| 813 | } |
| 814 | |
| 815 | bool C4ObjectList::ShiftContents(C4Object *pNewFirst) |
| 816 | { |
| 817 | // get link of new first (this ensures list is not empty) |
| 818 | C4ObjectLink *pNewFirstLnk = GetLink(pObj: pNewFirst); |
| 819 | if (!pNewFirstLnk) return false; |
| 820 | // already at front? |
| 821 | if (pNewFirstLnk == First) return true; |
| 822 | // sort it there: |
| 823 | // 1. Make cyclic list |
| 824 | Last->Next = First; First->Prev = Last; |
| 825 | // 2. Re-set first and last |
| 826 | First = pNewFirstLnk; |
| 827 | Last = pNewFirstLnk->Prev; |
| 828 | // 3. Uncycle list |
| 829 | First->Prev = Last->Next = nullptr; |
| 830 | // done, success |
| 831 | return true; |
| 832 | } |
| 833 | |
| 834 | void C4ObjectList::DeleteObjects() |
| 835 | { |
| 836 | // delete links and objects |
| 837 | while (First) |
| 838 | { |
| 839 | C4Object *pObj = First->Obj; |
| 840 | Remove(pObj); |
| 841 | delete pObj; |
| 842 | } |
| 843 | // reset mass |
| 844 | Mass = 0; |
| 845 | } |
| 846 | |
| 847 | // C4ObjectListIterator |
| 848 | |
| 849 | C4Object *C4ObjectListIterator::GetNext(int32_t *piCount, uint32_t dwCategory) |
| 850 | { |
| 851 | // end reached? |
| 852 | if (pCurrID == rList.end()) return nullptr; |
| 853 | // not yet started? |
| 854 | if (pCurr == rList.end()) |
| 855 | // then start at first ID list head |
| 856 | pCurr = pCurrID; |
| 857 | else |
| 858 | // next item |
| 859 | if (++pCurr == rList.end()) return nullptr; |
| 860 | // skip mismatched category |
| 861 | if (dwCategory) |
| 862 | while (!((*pCurr)->Category & dwCategory)) |
| 863 | if (++pCurr == rList.end()) return nullptr; |
| 864 | // next ID section reached? |
| 865 | if ((*pCurr)->id != (*pCurrID)->id) |
| 866 | pCurrID = pCurr; |
| 867 | else |
| 868 | { |
| 869 | // otherwise, it must be checked, whether this is a duplicate item already iterated |
| 870 | // if so, advance the list |
| 871 | for (C4ObjectList::iterator pCheck = pCurrID; pCheck != pCurr; ++pCheck) |
| 872 | if (!dwCategory || ((*pCheck)->Category & dwCategory)) |
| 873 | if ((*pCheck)->CanConcatPictureWith(pOtherObject: *pCurr)) |
| 874 | { |
| 875 | // next object of matching category |
| 876 | if (++pCurr == rList.end()) return nullptr; |
| 877 | if (dwCategory) |
| 878 | while (!((*pCurr)->Category & dwCategory)) |
| 879 | if (++pCurr == rList.end()) return nullptr; |
| 880 | // next ID chunk reached? |
| 881 | if ((*pCurr)->id != (*pCurrID)->id) |
| 882 | { |
| 883 | // then break here |
| 884 | pCurrID = pCurr; |
| 885 | break; |
| 886 | } |
| 887 | // restart check for next object |
| 888 | pCheck = pCurrID; |
| 889 | } |
| 890 | } |
| 891 | if (piCount) |
| 892 | { |
| 893 | // default count |
| 894 | *piCount = 1; |
| 895 | // add additional objects of same ID to the count |
| 896 | C4ObjectList::iterator pCheck(pCurr); |
| 897 | for (++pCheck; pCheck != rList.end() && (*pCheck)->id == (*pCurr)->id; ++pCheck) |
| 898 | if (!dwCategory || ((*pCheck)->Category & dwCategory)) |
| 899 | if ((*pCheck)->CanConcatPictureWith(pOtherObject: *pCurr)) |
| 900 | ++*piCount; |
| 901 | } |
| 902 | // return found object |
| 903 | return *pCurr; |
| 904 | } |
| 905 | |
| 906 | void C4ObjectList::UpdateScriptPointers() |
| 907 | { |
| 908 | for (C4ObjectLink *cLnk = First; cLnk; cLnk = cLnk->Next) |
| 909 | cLnk->Obj->UpdateScriptPointers(); |
| 910 | } |
| 911 | |
| 912 | struct C4ObjectListDumpHelper |
| 913 | { |
| 914 | C4ObjectList *pLst; |
| 915 | |
| 916 | void CompileFunc(StdCompiler *pComp) { pComp->Value(rStruct: mkNamingAdapt(rValue&: *pLst, szName: "Objects" )); } |
| 917 | |
| 918 | C4ObjectListDumpHelper(C4ObjectList *pLst) : pLst(pLst) {} |
| 919 | }; |
| 920 | |
| 921 | bool C4ObjectList::CheckSort(C4ObjectList *pList) |
| 922 | { |
| 923 | C4ObjectLink *cLnk = First, *cLnk2 = pList->First; |
| 924 | while (cLnk && cLnk->Obj->Unsorted) cLnk = cLnk->Next; |
| 925 | while (cLnk) |
| 926 | if (!cLnk2) |
| 927 | { |
| 928 | LogNTr(level: spdlog::level::err, message: "CheckSort failure" ); |
| 929 | spdlog::error(msg: DecompileToBuf<StdCompilerINIWrite>(SrcStruct: mkNamingAdapt(rValue: C4ObjectListDumpHelper(this), szName: "SectorList" ))); |
| 930 | spdlog::error(msg: DecompileToBuf<StdCompilerINIWrite>(SrcStruct: mkNamingAdapt(rValue: C4ObjectListDumpHelper(pList), szName: "MainList" ))); |
| 931 | return false; |
| 932 | } |
| 933 | else |
| 934 | { |
| 935 | if (cLnk->Obj == cLnk2->Obj) |
| 936 | { |
| 937 | cLnk = cLnk->Next; |
| 938 | while (cLnk && cLnk->Obj->Unsorted) cLnk = cLnk->Next; |
| 939 | } |
| 940 | cLnk2 = cLnk2->Next; |
| 941 | } |
| 942 | return true; |
| 943 | } |
| 944 | |
| 945 | void C4ObjectList::CheckCategorySort() |
| 946 | { |
| 947 | // debug: Check whether object list is sorted correctly |
| 948 | C4ObjectLink *cLnk, *cPrev = nullptr; |
| 949 | for (cLnk = First; cLnk && cLnk->Next; cLnk = cLnk->Next) |
| 950 | if (!cLnk->Obj->Unsorted && cLnk->Obj->Status) |
| 951 | { |
| 952 | if (cPrev) assert((cPrev->Obj->Category & C4D_SortLimit) >= (cLnk->Obj->Category & C4D_SortLimit)); |
| 953 | cPrev = cLnk; |
| 954 | } |
| 955 | } |
| 956 | |
| 957 | C4ObjectList::iterator::iterator(C4ObjectList &List, C4ObjectLink *C4ObjectLink::*const direction) : |
| 958 | List(List), pLink(direction == &C4ObjectLink::Next ? List.First : List.Last), direction{direction} |
| 959 | { |
| 960 | Next = List.AddIter(iter: this); |
| 961 | } |
| 962 | |
| 963 | C4ObjectList::iterator::iterator(C4ObjectList &List, C4ObjectLink *pLink, C4ObjectLink *C4ObjectLink::*const direction) : |
| 964 | List(List), pLink(pLink), direction{direction} |
| 965 | { |
| 966 | Next = List.AddIter(iter: this); |
| 967 | } |
| 968 | |
| 969 | C4ObjectList::iterator::iterator(const C4ObjectList::iterator &iter) : |
| 970 | List(iter.List), pLink(iter.pLink), Next(), direction{iter.direction} |
| 971 | { |
| 972 | Next = List.AddIter(iter: this); |
| 973 | } |
| 974 | |
| 975 | C4ObjectList::iterator::~iterator() |
| 976 | { |
| 977 | List.RemoveIter(iter: this); |
| 978 | } |
| 979 | |
| 980 | C4ObjectList::iterator &C4ObjectList::iterator::operator++() |
| 981 | { |
| 982 | pLink = pLink ? pLink->*direction : pLink; |
| 983 | return *this; |
| 984 | } |
| 985 | |
| 986 | C4Object *C4ObjectList::iterator::operator*() |
| 987 | { |
| 988 | return pLink ? pLink->Obj : nullptr; |
| 989 | } |
| 990 | |
| 991 | bool C4ObjectList::iterator::operator==(const iterator &iter) const |
| 992 | { |
| 993 | return &iter.List == &List && iter.pLink == pLink; |
| 994 | } |
| 995 | |
| 996 | bool C4ObjectList::iterator::operator==(std::default_sentinel_t) const noexcept |
| 997 | { |
| 998 | return pLink == nullptr; |
| 999 | } |
| 1000 | |
| 1001 | C4ObjectList::iterator &C4ObjectList::iterator::operator=(const iterator &iter) |
| 1002 | { |
| 1003 | // Can only assign iterators into the same list |
| 1004 | assert(&iter.List == &List); |
| 1005 | |
| 1006 | pLink = iter.pLink; |
| 1007 | return *this; |
| 1008 | } |
| 1009 | |
| 1010 | C4ObjectList::iterator C4ObjectList::begin() |
| 1011 | { |
| 1012 | return iterator(*this); |
| 1013 | } |
| 1014 | |
| 1015 | const C4ObjectList::iterator C4ObjectList::end() |
| 1016 | { |
| 1017 | return iterator(*this, nullptr, &C4ObjectLink::Next); |
| 1018 | } |
| 1019 | |
| 1020 | C4ObjectList::iterator C4ObjectList::BeginLast() |
| 1021 | { |
| 1022 | return iterator(*this, &C4ObjectLink::Prev); |
| 1023 | } |
| 1024 | |
| 1025 | std::default_sentinel_t C4ObjectList::EndLast() |
| 1026 | { |
| 1027 | return {}; |
| 1028 | } |
| 1029 | |
| 1030 | C4ObjectList::iterator *C4ObjectList::AddIter(iterator *iter) |
| 1031 | { |
| 1032 | iterator *r = FirstIter; |
| 1033 | FirstIter = iter; |
| 1034 | return r; |
| 1035 | } |
| 1036 | |
| 1037 | void C4ObjectList::RemoveIter(iterator *iter) |
| 1038 | { |
| 1039 | if (iter == FirstIter) |
| 1040 | FirstIter = iter->Next; |
| 1041 | else |
| 1042 | { |
| 1043 | iterator *i = FirstIter; |
| 1044 | while (i->Next && i->Next != iter) |
| 1045 | i = i->Next; |
| 1046 | i->Next = iter->Next; |
| 1047 | } |
| 1048 | } |
| 1049 | |