void C4SolidMask::Remove(bool fCauseInstability, bool fBackupAttachment) { // If put, restore background pixels from buffer // Not put if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; CheckConsistency(); // reput background pixels for (int ycnt = 0; ycnt < MaskPutRect.Hgt; ++ycnt) { BYTE *pPix = pSolidMaskMatBuff + (ycnt + MaskPutRect.ty) * MatBuffPitch + MaskPutRect.tx; for (int xcnt = 0; xcnt < MaskPutRect.Wdt; ++xcnt, ++pPix) // only if mask was used here if (*pPix != MCVehic) { // calc position in landscape int iTx = MaskPutRect.x + xcnt; int iTy = MaskPutRect.y + ycnt; // restore pixel here // The pPix-check ensures that only pixels that hads been overwritten by // this SolidMask are restored // Non-SolidMask-pixels should not happen here, because all relevant // landscape change routines should // temp remove SolidMasks before assert(_GBackPix(iTx, iTy) == MCVehic); _SBackPixIfMask(iTx, iTy, *pPix, MCVehic); // Instability if (fCauseInstability) Game.Landscape.CheckInstabilityRange(iTx, iTy); } } // Mask not put flag MaskPut = false; // update surrounding masks in that range C4TargetRect ClipRect; for (C4SolidMask *pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev) if (pSolid->MaskPut) if (pSolid->MaskPutRect.Overlap(MaskPutRect)) { // set clipping rect for all calls, since they may modify it ClipRect.Set(MaskPutRect.x, MaskPutRect.y, MaskPutRect.Wdt, MaskPutRect.Hgt, 0, 0); // doubled solidmask-pixels have just been removed in the clipped area! pSolid->MaskPut = false; // re-put the solidmask pSolid->Put(false, &ClipRect, false); } // backup attachment if desired: Backup old pos and all objects that attach to // or lie on the SolidMask if (fBackupAttachment) { MaskRemovalX = pForObject->x; MaskRemovalY = pForObject->y; iAttachingObjectsCount = 0; C4LArea SolidArea(&Game.Objects.Sectors, MaskPutRect.x - 1, MaskPutRect.y - 1, MaskPutRect.Wdt + 2, MaskPutRect.Hgt + 2); C4LSector *pSct; C4Object *pObj; for (C4ObjectList *pLst = SolidArea.FirstObjectShapes(&pSct); pLst; pLst = SolidArea.NextObjectShapes(pLst, &pSct)) for (C4ObjectLink *clnk = pLst->First; clnk; clnk = clnk->Next) if ((pObj = clnk->Obj) && pObj != pForObject && pObj->IsMoveableBySolidMask() && !pObj->Shape.CheckContact(pObj->x, pObj->y)) { // 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 :( int iVtx = 0; for (; iVtx < pObj->Shape.VtxNum; ++iVtx) if (pObj->Shape.GetVertexContact( iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->x, pObj->y, DensityProvider(pForObject, *this))) if (pObj->Shape.GetVertexContact( iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->x, pObj->y, DensityProvider(pForObject, *this))) break; if (iVtx == pObj->Shape.VtxNum) continue; // no contact // contact: Add object to list if (iAttachingObjectsCapacity == iAttachingObjectsCount) { iAttachingObjectsCapacity += 4; C4Object **ppNewAttachingObjects = new C4Object *[iAttachingObjectsCapacity]; if (iAttachingObjectsCount) memcpy(ppNewAttachingObjects, ppAttachingObjects, sizeof(C4Object *) * iAttachingObjectsCount); delete[] ppAttachingObjects; ppAttachingObjects = ppNewAttachingObjects; } ppAttachingObjects[iAttachingObjectsCount++] = pObj; } } CheckConsistency(); }
void C4SolidMask::Put(bool fCauseInstability, C4TargetRect *pClipRect, bool fRestoreAttachment) { // If not put, put mask to background, // storing background pixels in cSolidMask. // No mask if (!pSolidMask || !pSolidMaskMatBuff) { iAttachingObjectsCount = 0; return; } // Contained if (pForObject->Contained) { iAttachingObjectsCount = 0; return; } // Mask is put if (fCauseInstability) CheckConsistency(); bool RegularPut; if (!pClipRect) { // Regular Put: Update MaskPutRect and MaskPutRotation MaskPutRotation = pForObject->r; pClipRect = &MaskPutRect; RegularPut = true; } else { // Reput by C4SolidMask::Remove // Don't change MaskPutRotation or MaskPutRect // Intersect ClipRect with the MaskPutRect if (!pClipRect->ClipBy(MaskPutRect)) return; RegularPut = false; } // Lock mask surface int iPitch = pForObject->SolidMask.Wdt; int xcnt, ycnt, iTx, iTy; // Put mask pixels BYTE byPixel; // not rotated? if (!MaskPutRotation) { // calc put rect if (RegularPut) { int ox, oy; ox = pForObject->x + pForObject->Def->Shape.x + pForObject->SolidMask.tx; oy = pForObject->y + pForObject->Def->Shape.y + pForObject->SolidMask.ty; MaskPutRect.x = ox; if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.x = 0; } else MaskPutRect.tx = 0; MaskPutRect.y = oy; if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.y = 0; } else MaskPutRect.ty = 0; MaskPutRect.Wdt = Min<int32_t>(ox + pForObject->SolidMask.Wdt, GBackWdt) - MaskPutRect.x; MaskPutRect.Hgt = Min<int32_t>(oy + pForObject->SolidMask.Hgt, GBackHgt) - MaskPutRect.y; } // fill rect with mask for (ycnt = 0; ycnt < pClipRect->Hgt; ++ycnt) { BYTE *pPix = pSolidMask + (ycnt + pClipRect->ty) * pForObject->SolidMask.Wdt + pClipRect->tx; for (xcnt = 0; xcnt < pClipRect->Wdt; ++xcnt, ++pPix) { if (*pPix) { // solid mask present here // calc position in landscape iTx = pClipRect->x + xcnt; iTy = pClipRect->y + ycnt; // is background mat to be stored? always do this in the given rect if (!MaskPut) { // get background pixel byPixel = GBackPix(iTx, iTy); // store it. If MCVehic, also store in initial put, but won't be // used in restore // do not overwrite current value in re-put issued by // SolidMask-remover if (byPixel != MCVehic || RegularPut) pSolidMaskMatBuff[(ycnt + pClipRect->ty) * MatBuffPitch + xcnt + pClipRect->tx] = byPixel; } // and set mask _SBackPix(iTx, iTy, MCVehic); } else // no SolidMask: mark buffer as unused here if (!MaskPut) pSolidMaskMatBuff[(ycnt + pClipRect->ty) * MatBuffPitch + xcnt + pClipRect->tx] = MCVehic; } } } else { // calc matrix for given rotation FIXED Ma1 = Cos(itofix(-MaskPutRotation)), Ma2 = -Sin(itofix(-MaskPutRotation)), Mb1 = Sin(itofix(-MaskPutRotation)), Mb2 = Cos(itofix(-MaskPutRotation)); // get upper-left corner of landscape copy rect int centerx = pForObject->Def->Shape.x + pForObject->SolidMask.tx + pForObject->SolidMask.Wdt / 2; int centery = pForObject->Def->Shape.y + pForObject->SolidMask.ty + pForObject->SolidMask.Hgt / 2; int xstart = pForObject->x + fixtoi(Ma1 * itofix(centerx) - Ma2 * itofix(centery)) - MatBuffPitch / 2; int ystart = pForObject->y + fixtoi(-Mb1 * itofix(centerx) + Mb2 * itofix(centery)) - MatBuffPitch / 2; // store put rect if (RegularPut) { MaskPutRect.x = xstart; if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.Wdt = MaskPutRect.x; MaskPutRect.x = 0; } else { MaskPutRect.tx = 0; MaskPutRect.Wdt = 0; } MaskPutRect.y = ystart; if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.Hgt = MaskPutRect.y; MaskPutRect.y = 0; } else { MaskPutRect.ty = 0; MaskPutRect.Hgt = 0; } MaskPutRect.Wdt = Min<int32_t>(xstart + MatBuffPitch, GBackWdt) - MaskPutRect.x; MaskPutRect.Hgt = Min<int32_t>(ystart + MatBuffPitch, GBackHgt) - MaskPutRect.y; } // go through clipping rect const FIXED y0 = itofix(pClipRect->ty - MatBuffPitch / 2); const FIXED x0 = itofix(pClipRect->tx - MatBuffPitch / 2); iTy = pClipRect->y; int w = pForObject->SolidMask.Wdt; int h = pForObject->SolidMask.Hgt; FIXED ya = y0 * Ma2; FIXED yb = y0 * Mb2; for (ycnt = 0; ycnt < pClipRect->Hgt; ycnt++) { iTx = pClipRect->x; int i = (ycnt + pClipRect->ty) * MatBuffPitch + pClipRect->tx; FIXED xa = x0 * Ma1; FIXED xb = x0 * Mb1; for (xcnt = 0; xcnt < pClipRect->Wdt; xcnt++) { // calc position in solidmask buffer int iMx = fixtoi(xa + ya) + w / 2; int iMy = fixtoi(xb + yb) + h / 2; // in bounds? and solidmask? if (iMx >= 0 && iMy >= 0 && iMx < w && iMy < h && pSolidMask[iMy * iPitch + iMx]) { // is background mat to be stored? if (!MaskPut) { // get background pixel byPixel = _GBackPix(iTx, iTy); // store it. If MCVehic, also store in initial put, but won't be // used in restore // do not overwrite current value in re-put issued by // SolidMask-remover if (byPixel != MCVehic || RegularPut) pSolidMaskMatBuff[i + xcnt] = byPixel; } // set mask pix _SBackPix(iTx, iTy, MCVehic); } else if (!MaskPut) // mark pix as unused in buf pSolidMaskMatBuff[i + xcnt] = MCVehic; xa += Ma1; xb += Mb1; ++iTx; } ya += Ma2; yb += Mb2; ++iTy; } } // Store mask put status MaskPut = TRUE; // restore attached object positions if moved if (fRestoreAttachment && iAttachingObjectsCount) { int32_t dx = pForObject->x - MaskRemovalX; int32_t dy = pForObject->y - MaskRemovalY; if (dx | dy) for (int i = 0; i < iAttachingObjectsCount; ++i) { C4Object *pObj = ppAttachingObjects[i]; if (pObj->IsMoveableBySolidMask()) if (!pObj->Shape.ContactCheck(pObj->x + dx, pObj->y + dy)) if (pObj->iLastAttachMovementFrame != Game.FrameCounter) { pObj->iLastAttachMovementFrame = Game.FrameCounter; pObj->MovePosition(dx, dy); } } iAttachingObjectsCount = 0; } if (fCauseInstability) CheckConsistency(); }