| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2003, Sven2 |
| 6 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 7 | * |
| 8 | * Distributed under the terms of the ISC license; see accompanying file |
| 9 | * "COPYING" for details. |
| 10 | * |
| 11 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 12 | * See accompanying file "TRADEMARK" for details. |
| 13 | * |
| 14 | * To redistribute this file separately, substitute the full license texts |
| 15 | * for the above references. |
| 16 | */ |
| 17 | |
| 18 | /* Solid areas of objects, put into the landscape */ |
| 19 | |
| 20 | #include <C4Include.h> |
| 21 | #include <C4SolidMask.h> |
| 22 | |
| 23 | #include <C4Object.h> |
| 24 | #include <C4Wrappers.h> |
| 25 | |
| 26 | void C4SolidMask::Put(bool fCauseInstability, C4TargetRect *pClipRect, bool fRestoreAttachment) |
| 27 | { |
| 28 | // If not put, put mask to background, |
| 29 | // storing background pixels in cSolidMask. |
| 30 | |
| 31 | // No mask |
| 32 | if (!pSolidMask || !pSolidMaskMatBuff) { iAttachingObjectsCount = 0; return; } |
| 33 | // Contained |
| 34 | if (pForObject->Contained) { iAttachingObjectsCount = 0; return; } |
| 35 | // Mask is put |
| 36 | if (fCauseInstability) CheckConsistency(); |
| 37 | |
| 38 | bool RegularPut; |
| 39 | if (!pClipRect) |
| 40 | { |
| 41 | // Regular Put: Update MaskPutRect and MaskPutRotation |
| 42 | MaskPutRotation = pForObject->r; |
| 43 | pClipRect = &MaskPutRect; |
| 44 | RegularPut = true; |
| 45 | } |
| 46 | else |
| 47 | { |
| 48 | // Reput by C4SolidMask::Remove |
| 49 | // Don't change MaskPutRotation or MaskPutRect |
| 50 | // Intersect ClipRect with the MaskPutRect |
| 51 | if (!pClipRect->ClipBy(rClip&: MaskPutRect)) return; |
| 52 | RegularPut = false; |
| 53 | } |
| 54 | // Lock mask surface |
| 55 | int iPitch = pForObject->SolidMask.Wdt; |
| 56 | int xcnt, ycnt, iTx, iTy; |
| 57 | // Put mask pixels |
| 58 | uint8_t byPixel; |
| 59 | // not rotated? |
| 60 | if (!MaskPutRotation) |
| 61 | { |
| 62 | // calc put rect |
| 63 | if (RegularPut) |
| 64 | { |
| 65 | int ox, oy; |
| 66 | ox = pForObject->x + pForObject->Def->Shape.x + pForObject->SolidMask.tx; |
| 67 | oy = pForObject->y + pForObject->Def->Shape.y + pForObject->SolidMask.ty; |
| 68 | MaskPutRect.x = ox; |
| 69 | if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.x = 0; } |
| 70 | else MaskPutRect.tx = 0; |
| 71 | MaskPutRect.y = oy; |
| 72 | if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.y = 0; } |
| 73 | else MaskPutRect.ty = 0; |
| 74 | MaskPutRect.Wdt = std::min<int32_t>(a: ox + pForObject->SolidMask.Wdt, GBackWdt) - MaskPutRect.x; |
| 75 | MaskPutRect.Hgt = std::min<int32_t>(a: oy + pForObject->SolidMask.Hgt, GBackHgt) - MaskPutRect.y; |
| 76 | } |
| 77 | // fill rect with mask |
| 78 | for (ycnt = 0; ycnt < pClipRect->Hgt; ++ycnt) |
| 79 | { |
| 80 | uint8_t *pPix = pSolidMask + (ycnt + pClipRect->ty) * pForObject->SolidMask.Wdt + pClipRect->tx; |
| 81 | for (xcnt = 0; xcnt < pClipRect->Wdt; ++xcnt, ++pPix) |
| 82 | { |
| 83 | if (*pPix) |
| 84 | { |
| 85 | // solid mask present here |
| 86 | // calc position in landscape |
| 87 | iTx = pClipRect->x + xcnt; iTy = pClipRect->y + ycnt; |
| 88 | // is background mat to be stored? always do this in the given rect |
| 89 | if (!MaskPut) |
| 90 | { |
| 91 | // get background pixel |
| 92 | byPixel = GBackPix(x: iTx, y: iTy); |
| 93 | // store it. If MCVehic, also store in initial put, but won't be used in restore |
| 94 | // do not overwrite current value in re-put issued by SolidMask-remover |
| 95 | if (byPixel != MCVehic || RegularPut) |
| 96 | pSolidMaskMatBuff[(ycnt + pClipRect->ty) * MatBuffPitch + xcnt + pClipRect->tx] = byPixel; |
| 97 | } |
| 98 | // and set mask |
| 99 | _SBackPix(x: iTx, y: iTy, npix: MCVehic); |
| 100 | } |
| 101 | else |
| 102 | // no SolidMask: mark buffer as unused here |
| 103 | if (!MaskPut) |
| 104 | pSolidMaskMatBuff[(ycnt + pClipRect->ty) * MatBuffPitch + xcnt + pClipRect->tx] = MCVehic; |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | else |
| 109 | { |
| 110 | // calc matrix for given rotation |
| 111 | C4Fixed Ma1 = Cos(fAngle: itofix(x: -MaskPutRotation)), Ma2 = -Sin(fAngle: itofix(x: -MaskPutRotation)), |
| 112 | Mb1 = Sin(fAngle: itofix(x: -MaskPutRotation)), Mb2 = Cos(fAngle: itofix(x: -MaskPutRotation)); |
| 113 | // get upper-left corner of landscape copy rect |
| 114 | int centerx = pForObject->Def->Shape.x + pForObject->SolidMask.tx + pForObject->SolidMask.Wdt / 2; |
| 115 | int centery = pForObject->Def->Shape.y + pForObject->SolidMask.ty + pForObject->SolidMask.Hgt / 2; |
| 116 | int xstart = pForObject->x + fixtoi(x: Ma1 * itofix(x: centerx) - Ma2 * itofix(x: centery)) - MatBuffPitch / 2; |
| 117 | int ystart = pForObject->y + fixtoi(x: -Mb1 * itofix(x: centerx) + Mb2 * itofix(x: centery)) - MatBuffPitch / 2; |
| 118 | // store put rect |
| 119 | if (RegularPut) |
| 120 | { |
| 121 | MaskPutRect.x = xstart; |
| 122 | if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.Wdt = MaskPutRect.x; MaskPutRect.x = 0; } |
| 123 | else { MaskPutRect.tx = 0; MaskPutRect.Wdt = 0; } |
| 124 | MaskPutRect.y = ystart; |
| 125 | if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.Hgt = MaskPutRect.y; MaskPutRect.y = 0; } |
| 126 | else { MaskPutRect.ty = 0; MaskPutRect.Hgt = 0; } |
| 127 | MaskPutRect.Wdt = std::min<int32_t>(a: xstart + MatBuffPitch, GBackWdt) - MaskPutRect.x; |
| 128 | MaskPutRect.Hgt = std::min<int32_t>(a: ystart + MatBuffPitch, GBackHgt) - MaskPutRect.y; |
| 129 | } |
| 130 | // go through clipping rect |
| 131 | const C4Fixed y0 = itofix(x: pClipRect->ty - MatBuffPitch / 2); |
| 132 | const C4Fixed x0 = itofix(x: pClipRect->tx - MatBuffPitch / 2); |
| 133 | iTy = pClipRect->y; |
| 134 | int w = pForObject->SolidMask.Wdt; |
| 135 | int h = pForObject->SolidMask.Hgt; |
| 136 | C4Fixed ya = y0 * Ma2; |
| 137 | C4Fixed yb = y0 * Mb2; |
| 138 | for (ycnt = 0; ycnt < pClipRect->Hgt; ycnt++) |
| 139 | { |
| 140 | iTx = pClipRect->x; |
| 141 | int i = (ycnt + pClipRect->ty) * MatBuffPitch + pClipRect->tx; |
| 142 | C4Fixed xa = x0 * Ma1; |
| 143 | C4Fixed xb = x0 * Mb1; |
| 144 | for (xcnt = 0; xcnt < pClipRect->Wdt; xcnt++) |
| 145 | { |
| 146 | // calc position in solidmask buffer |
| 147 | int iMx = fixtoi(x: xa + ya) + w / 2; |
| 148 | int iMy = fixtoi(x: xb + yb) + h / 2; |
| 149 | // in bounds? and solidmask? |
| 150 | if (iMx >= 0 && iMy >= 0 && iMx < w && iMy < h && pSolidMask[iMy * iPitch + iMx]) |
| 151 | { |
| 152 | // is background mat to be stored? |
| 153 | if (!MaskPut) |
| 154 | { |
| 155 | // get background pixel |
| 156 | byPixel = _GBackPix(x: iTx, y: iTy); |
| 157 | // store it. If MCVehic, also store in initial put, but won't be used in restore |
| 158 | // do not overwrite current value in re-put issued by SolidMask-remover |
| 159 | if (byPixel != MCVehic || RegularPut) |
| 160 | pSolidMaskMatBuff[i + xcnt] = byPixel; |
| 161 | } |
| 162 | // set mask pix |
| 163 | _SBackPix(x: iTx, y: iTy, npix: MCVehic); |
| 164 | } |
| 165 | else if (!MaskPut) |
| 166 | // mark pix as unused in buf |
| 167 | pSolidMaskMatBuff[i + xcnt] = MCVehic; |
| 168 | xa += Ma1; xb += Mb1; |
| 169 | ++iTx; |
| 170 | } |
| 171 | ya += Ma2; yb += Mb2; |
| 172 | ++iTy; |
| 173 | } |
| 174 | } |
| 175 | // Store mask put status |
| 176 | MaskPut = true; |
| 177 | // restore attached object positions if moved |
| 178 | if (fRestoreAttachment && iAttachingObjectsCount) |
| 179 | { |
| 180 | int32_t dx = pForObject->x - MaskRemovalX; |
| 181 | int32_t dy = pForObject->y - MaskRemovalY; |
| 182 | if (dx | dy) |
| 183 | for (int i = 0; i < iAttachingObjectsCount; ++i) |
| 184 | { |
| 185 | C4Object *pObj = ppAttachingObjects[i]; |
| 186 | if (pObj->IsMoveableBySolidMask()) |
| 187 | if (!pObj->Shape.ContactCheck(cx: pObj->x + dx, cy: pObj->y + dy)) |
| 188 | if (pObj->iLastAttachMovementFrame != Game.FrameCounter) |
| 189 | { |
| 190 | pObj->iLastAttachMovementFrame = Game.FrameCounter; |
| 191 | pObj->MovePosition(dx, dy); |
| 192 | } |
| 193 | } |
| 194 | iAttachingObjectsCount = 0; |
| 195 | } |
| 196 | |
| 197 | if (fCauseInstability) CheckConsistency(); |
| 198 | } |
| 199 | |
| 200 | int32_t C4SolidMask::DensityProvider::GetDensity(int32_t x, int32_t y) const |
| 201 | { |
| 202 | // outside SolidMask: free |
| 203 | x -= rSolidMaskData.MaskPutRect.x; |
| 204 | y -= rSolidMaskData.MaskPutRect.y; |
| 205 | if (!Inside<int32_t>(ival: x, lbound: 0, rbound: rSolidMaskData.MaskPutRect.Wdt - 1) |
| 206 | || !Inside<int32_t>(ival: y, lbound: 0, rbound: rSolidMaskData.MaskPutRect.Hgt - 1)) |
| 207 | return 0; |
| 208 | // check put mask. Easy for unrotated |
| 209 | uint8_t *pPix; |
| 210 | if (!rSolidMaskData.MaskPutRotation) |
| 211 | { |
| 212 | pPix = rSolidMaskData.pSolidMask + (y + rSolidMaskData.MaskPutRect.ty) * rSolidMaskData.pForObject->SolidMask.Wdt + rSolidMaskData.MaskPutRect.tx + x; |
| 213 | if (*pPix == 0xff) |
| 214 | return C4M_Solid; |
| 215 | else |
| 216 | return 0; |
| 217 | } |
| 218 | else |
| 219 | { |
| 220 | // Using put-buffer for rotated masks |
| 221 | // for SolidMask-pixels not put because there was another SolidMask already, this will not return solid |
| 222 | pPix = rSolidMaskData.pSolidMaskMatBuff + (y + rSolidMaskData.MaskPutRect.ty) * rSolidMaskData.MatBuffPitch + rSolidMaskData.MaskPutRect.tx + x; |
| 223 | if (*pPix == MCVehic) |
| 224 | return 0; |
| 225 | else |
| 226 | return C4M_Solid; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | void C4SolidMask::Remove(bool fCauseInstability, bool fBackupAttachment) |
| 231 | { |
| 232 | // If put, restore background pixels from buffer |
| 233 | |
| 234 | // Not put |
| 235 | if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; |
| 236 | |
| 237 | CheckConsistency(); |
| 238 | |
| 239 | // reput background pixels |
| 240 | for (int ycnt = 0; ycnt < MaskPutRect.Hgt; ++ycnt) |
| 241 | { |
| 242 | uint8_t *pPix = pSolidMaskMatBuff + (ycnt + MaskPutRect.ty) * MatBuffPitch + MaskPutRect.tx; |
| 243 | for (int xcnt = 0; xcnt < MaskPutRect.Wdt; ++xcnt, ++pPix) |
| 244 | // only if mask was used here |
| 245 | if (*pPix != MCVehic) |
| 246 | { |
| 247 | // calc position in landscape |
| 248 | int iTx = MaskPutRect.x + xcnt; int iTy = MaskPutRect.y + ycnt; |
| 249 | // restore pixel here |
| 250 | // The pPix-check ensures that only pixels that hads been overwritten by this SolidMask are restored |
| 251 | // Non-SolidMask-pixels should not happen here, because all relevant landscape change routines should |
| 252 | // temp remove SolidMasks before |
| 253 | assert(_GBackPix(iTx, iTy) == MCVehic); |
| 254 | _SBackPixIfMask(x: iTx, y: iTy, npix: *pPix, nMask: MCVehic); |
| 255 | // Instability |
| 256 | if (fCauseInstability) |
| 257 | Game.Landscape.CheckInstabilityRange(tx: iTx, ty: iTy); |
| 258 | } |
| 259 | } |
| 260 | // Mask not put flag |
| 261 | MaskPut = false; |
| 262 | // update surrounding masks in that range |
| 263 | C4TargetRect ClipRect; |
| 264 | for (C4SolidMask *pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev) |
| 265 | if (pSolid->MaskPut) if (pSolid->MaskPutRect.Overlap(rTarget&: MaskPutRect)) |
| 266 | { |
| 267 | // set clipping rect for all calls, since they may modify it |
| 268 | ClipRect.Set(iX: MaskPutRect.x, iY: MaskPutRect.y, iWdt: MaskPutRect.Wdt, iHgt: MaskPutRect.Hgt, iTX: 0, iTY: 0); |
| 269 | // doubled solidmask-pixels have just been removed in the clipped area! |
| 270 | pSolid->MaskPut = false; |
| 271 | // re-put the solidmask |
| 272 | pSolid->Put(fCauseInstability: false, pClipRect: &ClipRect, fRestoreAttachment: false); |
| 273 | } |
| 274 | |
| 275 | // backup attachment if desired: Backup old pos and all objects that attach to or lie on the SolidMask |
| 276 | if (fBackupAttachment) |
| 277 | { |
| 278 | MaskRemovalX = pForObject->x; |
| 279 | MaskRemovalY = pForObject->y; |
| 280 | iAttachingObjectsCount = 0; |
| 281 | C4LArea SolidArea(&Game.Objects.Sectors, MaskPutRect.x - 1, MaskPutRect.y - 1, MaskPutRect.Wdt + 2, MaskPutRect.Hgt + 2); |
| 282 | C4LSector *pSct; C4Object *pObj; |
| 283 | for (C4ObjectList *pLst = SolidArea.FirstObjectShapes(ppSct: &pSct); pLst; pLst = SolidArea.NextObjectShapes(pPrev: pLst, ppSct: &pSct)) |
| 284 | for (C4ObjectLink *clnk = pLst->First; clnk; clnk = clnk->Next) |
| 285 | if ((pObj = clnk->Obj) && pObj != pForObject && pObj->IsMoveableBySolidMask() && !pObj->Shape.CheckContact(cx: pObj->x, cy: pObj->y)) |
| 286 | { |
| 287 | // check for any contact to own SolidMask - attach-directions, bottom - "stuck" (CNAT_Center) is ignored, because that causes problems with things being stuck in basements :( |
| 288 | int iVtx = 0; |
| 289 | for (; iVtx < pObj->Shape.VtxNum; ++iVtx) |
| 290 | if (pObj->Shape.GetVertexContact(iVtx, dwCheckMask: pObj->Action.t_attach | CNAT_Bottom, tx: pObj->x, ty: pObj->y, rDensityProvider: DensityProvider(pForObject, *this))) |
| 291 | if (pObj->Shape.GetVertexContact(iVtx, dwCheckMask: pObj->Action.t_attach | CNAT_Bottom, tx: pObj->x, ty: pObj->y, rDensityProvider: DensityProvider(pForObject, *this))) |
| 292 | break; |
| 293 | if (iVtx == pObj->Shape.VtxNum) continue; // no contact |
| 294 | // contact: Add object to list |
| 295 | if (iAttachingObjectsCapacity == iAttachingObjectsCount) |
| 296 | { |
| 297 | iAttachingObjectsCapacity += 4; |
| 298 | C4Object **ppNewAttachingObjects = new C4Object *[iAttachingObjectsCapacity]; |
| 299 | if (iAttachingObjectsCount) memcpy(dest: ppNewAttachingObjects, src: ppAttachingObjects, n: sizeof(C4Object *) * iAttachingObjectsCount); |
| 300 | delete[] ppAttachingObjects; |
| 301 | ppAttachingObjects = ppNewAttachingObjects; |
| 302 | } |
| 303 | ppAttachingObjects[iAttachingObjectsCount++] = pObj; |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | CheckConsistency(); |
| 308 | } |
| 309 | |
| 310 | void C4SolidMask::Clear() |
| 311 | { |
| 312 | // free mask+mat-buffer |
| 313 | delete[] pSolidMask; pSolidMask = nullptr; |
| 314 | delete[] pSolidMaskMatBuff; pSolidMaskMatBuff = nullptr; |
| 315 | // safety: mask cannot be removed now |
| 316 | MaskPut = false; |
| 317 | // clear attaching objects |
| 318 | delete[] ppAttachingObjects; ppAttachingObjects = nullptr; |
| 319 | iAttachingObjectsCount = iAttachingObjectsCapacity = 0; |
| 320 | } |
| 321 | |
| 322 | void C4SolidMask::RemoveTemporary(C4Rect where) |
| 323 | { |
| 324 | if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; |
| 325 | where.Intersect(r2: MaskPutRect); |
| 326 | // reput background pixels |
| 327 | for (int y = where.y; y < where.y + where.Hgt; ++y) |
| 328 | { |
| 329 | for (int x = where.x; x < where.x + where.Wdt; ++x) |
| 330 | { |
| 331 | uint8_t *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx; |
| 332 | // only if mask was used here |
| 333 | if (*pPix != MCVehic) |
| 334 | { |
| 335 | // restore |
| 336 | assert(GBackPix(x, y) == MCVehic); |
| 337 | _SBackPix(x, y, npix: *pPix); |
| 338 | } |
| 339 | } |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | void C4SolidMask::PutTemporary(C4Rect where) |
| 344 | { |
| 345 | if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; |
| 346 | where.Intersect(r2: MaskPutRect); |
| 347 | // reput vehicle pixels |
| 348 | for (int y = where.y; y < where.y + where.Hgt; ++y) |
| 349 | { |
| 350 | for (int x = where.x; x < where.x + where.Wdt; ++x) |
| 351 | { |
| 352 | uint8_t *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx; |
| 353 | // only if mask was used here |
| 354 | if (*pPix != MCVehic) |
| 355 | { |
| 356 | // put |
| 357 | assert(GBackPix(x, y) == *pPix); |
| 358 | _SBackPix(x, y, npix: MCVehic); |
| 359 | } |
| 360 | } |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | void C4SolidMask::Repair(C4Rect where) |
| 365 | { |
| 366 | if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; |
| 367 | where.Intersect(r2: MaskPutRect); |
| 368 | // reput vehicle pixels |
| 369 | for (int y = where.y; y < where.y + where.Hgt; ++y) |
| 370 | { |
| 371 | for (int x = where.x; x < where.x + where.Wdt; ++x) |
| 372 | { |
| 373 | uint8_t *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx; |
| 374 | // only if mask was used here |
| 375 | if (*pPix != MCVehic) |
| 376 | { |
| 377 | // record changed landscape in MatBuff |
| 378 | *pPix = GBackPix(x, y); |
| 379 | // put |
| 380 | _SBackPix(x, y, npix: MCVehic); |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | C4SolidMask::C4SolidMask(C4Object *pForObject) : pForObject(pForObject) |
| 387 | { |
| 388 | // zero fields |
| 389 | MaskPut = false; |
| 390 | MaskPutRotation = 0; |
| 391 | MaskRemovalX = MaskRemovalY = 0; |
| 392 | ppAttachingObjects = nullptr; |
| 393 | iAttachingObjectsCount = iAttachingObjectsCapacity = 0; |
| 394 | // Update linked list |
| 395 | Next = nullptr; |
| 396 | Prev = Last; |
| 397 | Last = this; |
| 398 | if (Prev) Prev->Next = this; |
| 399 | else First = this; |
| 400 | // copy solid mask from bitmap |
| 401 | int iNeededBufSize = pForObject->SolidMask.Wdt * pForObject->SolidMask.Hgt; |
| 402 | pSolidMask = new uint8_t[iNeededBufSize]; |
| 403 | C4Surface *sfcBitmap = pForObject->GetGraphics()->GetBitmap(); |
| 404 | if (!sfcBitmap->Lock()) return; |
| 405 | |
| 406 | int xcnt, ycnt; |
| 407 | for (ycnt = 0; ycnt < pForObject->SolidMask.Hgt; ycnt++) |
| 408 | for (xcnt = 0; xcnt < pForObject->SolidMask.Wdt; xcnt++) |
| 409 | { |
| 410 | // Solid mask target x/y is relative to def bitmap top-left, not object center. |
| 411 | pSolidMask[xcnt + ycnt * pForObject->SolidMask.Wdt] = sfcBitmap->IsPixTransparent(iX: pForObject->SolidMask.x + xcnt, iY: pForObject->SolidMask.y + ycnt) ? 0x00 : 0xff; |
| 412 | } |
| 413 | // create mat buff to store the material replaced by the solid mask |
| 414 | // the upper left corner is here the [objpos]+rot([shapexy]+[targetxy]+[realWH]/2)-maxWH/2 |
| 415 | MatBuffPitch = static_cast<int>(sqrt(x: double(pForObject->SolidMask.Wdt * pForObject->SolidMask.Wdt + pForObject->SolidMask.Hgt * pForObject->SolidMask.Hgt))) + 1; |
| 416 | pSolidMaskMatBuff = new uint8_t[MatBuffPitch * MatBuffPitch]{}; |
| 417 | sfcBitmap->Unlock(noUpload: true); |
| 418 | } |
| 419 | |
| 420 | C4SolidMask::~C4SolidMask() |
| 421 | { |
| 422 | // Update linked list |
| 423 | if (Next) Next->Prev = Prev; |
| 424 | if (Prev) Prev->Next = Next; |
| 425 | if (First == this) First = Next; |
| 426 | if (Last == this) Last = Prev; |
| 427 | // clear fields |
| 428 | Clear(); |
| 429 | } |
| 430 | |
| 431 | C4SolidMask *C4SolidMask::First = nullptr; |
| 432 | C4SolidMask *C4SolidMask::Last = nullptr; |
| 433 | |
| 434 | #ifdef SOLIDMASK_DEBUG |
| 435 | |
| 436 | bool C4SolidMask::CheckConsistency() |
| 437 | { |
| 438 | C4Rect SolidMaskRect(0, 0, GBackWdt, GBackHgt); |
| 439 | C4SolidMask *pSolid; |
| 440 | for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev) |
| 441 | { |
| 442 | pSolid->RemoveTemporary(SolidMaskRect); |
| 443 | } |
| 444 | assert(!Game.Landscape.MatCount[MVehic]); |
| 445 | // Restore Solidmasks |
| 446 | for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next) |
| 447 | { |
| 448 | pSolid->PutTemporary(SolidMaskRect); |
| 449 | } |
| 450 | return true; |
| 451 | } |
| 452 | |
| 453 | #endif |
| 454 | |