1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk 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 /* Solid areas of objects, put into the landscape */
18 
19 #include "C4Include.h"
20 #include "landscape/C4SolidMask.h"
21 
22 #include "graphics/C4DrawGL.h"
23 #include "graphics/CSurface8.h"
24 #include "graphics/StdPNG.h"
25 #include "landscape/C4Landscape.h"
26 #include "landscape/C4Material.h"
27 #include "object/C4Def.h"
28 #include "object/C4GameObjects.h"
29 #include "object/C4Object.h"
30 
31 
Put(bool fCauseInstability,C4TargetRect * pClipRect,bool fRestoreAttachment)32 void C4SolidMask::Put(bool fCauseInstability, C4TargetRect *pClipRect, bool fRestoreAttachment)
33 {
34 	// If not put, put mask to background,
35 	// storing background pixels in cSolidMask.
36 
37 	// No mask
38 	if (!pForObject || !pForObject->Def || !pForObject->Def->pSolidMask || !pSolidMaskMatBuff) { iAttachingObjectsCount = 0; return; }
39 	// Contained
40 	if (pForObject->Contained) { iAttachingObjectsCount = 0; return; }
41 	// Mask is put
42 	if (fCauseInstability) CheckConsistency();
43 
44 	bool RegularPut;
45 	if (!pClipRect)
46 	{
47 		// Regular Put: Update MaskPutRect and MaskPutRotation
48 		MaskPutRotation = pForObject->GetR();
49 		pClipRect = &MaskPutRect;
50 		RegularPut = true;
51 	}
52 	else
53 	{
54 		// Reput by C4SolidMask::Remove
55 		// Don't change MaskPutRotation or MaskPutRect
56 		// Intersect ClipRect with the MaskPutRect
57 		if (!pClipRect->ClipBy(MaskPutRect)) return;
58 		RegularPut = false;
59 	}
60 	// Get mask surface
61 	CSurface8 *pSolidMask = pForObject->Def->pSolidMask;
62 	// Put mask pixels
63 	int xcnt,ycnt,iTx,iTy;
64 	BYTE byPixel;
65 	// not rotated?
66 	if (!MaskPutRotation)
67 	{
68 		// calc put rect
69 		if (RegularPut)
70 		{
71 			int ox, oy;
72 			ox = pForObject->GetX() + pForObject->Def->Shape.x + pForObject->SolidMask.tx;
73 			oy = pForObject->GetY() + pForObject->Def->Shape.y + pForObject->SolidMask.ty;
74 			MaskPutRect.x = ox;
75 			if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.x = 0; }
76 			else MaskPutRect.tx = 0;
77 			MaskPutRect.y = oy;
78 			if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.y = 0; }
79 			else MaskPutRect.ty = 0;
80 			MaskPutRect.Wdt = std::min<int32_t>(ox + pForObject->SolidMask.Wdt, ::Landscape.GetWidth()) - MaskPutRect.x;
81 			MaskPutRect.Hgt = std::min<int32_t>(oy + pForObject->SolidMask.Hgt, ::Landscape.GetHeight()) - MaskPutRect.y;
82 		}
83 		// fill rect with mask
84 		for (ycnt=0; ycnt<pClipRect->Hgt; ++ycnt)
85 		{
86 			BYTE *pPix=pSolidMask->Bits + (ycnt+pClipRect->ty+pForObject->SolidMask.y)*pSolidMask->Pitch + pClipRect->tx + pForObject->SolidMask.x;
87 			for (xcnt=0; xcnt<pClipRect->Wdt; ++xcnt,++pPix)
88 			{
89 				if (*pPix)
90 				{
91 					// solid mask present here
92 					// calc position in landscape
93 					iTx=pClipRect->x+xcnt; iTy=pClipRect->y+ycnt;
94 					// is background mat to be stored? always do this in the given rect
95 					if (!MaskPut)
96 					{
97 						// get background pixel
98 						byPixel=::Landscape.GetPix(iTx,iTy);
99 						// store it. If MCVehic, also store in initial put, but won't be used in restore
100 						// do not overwrite current value in re-put issued by SolidMask-remover
101 						if (!IsSomeVehicle(byPixel) || RegularPut)
102 							pSolidMaskMatBuff[(ycnt+pClipRect->ty)*MatBuffPitch+xcnt+pClipRect->tx]=byPixel;
103 					}
104 					// and set mask
105 					::Landscape.SetPix2(iTx,iTy,MaskMaterial,::Landscape.Transparent);
106 				}
107 				else
108 					// no SolidMask: mark buffer as unused here
109 					if (!MaskPut)
110 						pSolidMaskMatBuff[(ycnt+pClipRect->ty)*MatBuffPitch+xcnt+pClipRect->tx]=MCVehic;
111 			}
112 		}
113 	}
114 	else
115 	{
116 		// calc matrix for given rotation
117 		C4Real Ma1 = Cos(itofix(-MaskPutRotation)), Ma2 = -Sin(itofix(-MaskPutRotation)),
118 		            Mb1 = Sin(itofix(-MaskPutRotation)), Mb2 = Cos(itofix(-MaskPutRotation));
119 		// get upper-left corner of landscape copy rect
120 		int centerx = pForObject->Def->Shape.x + pForObject->SolidMask.tx + pForObject->SolidMask.Wdt / 2;
121 		int centery = pForObject->Def->Shape.y + pForObject->SolidMask.ty + pForObject->SolidMask.Hgt / 2;
122 		int xstart = pForObject->GetX() + fixtoi(Ma1 * itofix(centerx) - Ma2 * itofix(centery)) - MatBuffPitch / 2;
123 		int ystart = pForObject->GetY() + fixtoi(-Mb1 * itofix(centerx) + Mb2 * itofix(centery)) - MatBuffPitch / 2;
124 		// store put rect
125 		if (RegularPut)
126 		{
127 			MaskPutRect.x = xstart;
128 			if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.Wdt = MaskPutRect.x; MaskPutRect.x = 0; }
129 			else { MaskPutRect.tx = 0; MaskPutRect.Wdt = 0; }
130 			MaskPutRect.y = ystart;
131 			if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.Hgt = MaskPutRect.y; MaskPutRect.y = 0; }
132 			else { MaskPutRect.ty = 0; MaskPutRect.Hgt = 0; }
133 			MaskPutRect.Wdt = std::min<int32_t>(xstart + MatBuffPitch, ::Landscape.GetWidth()) - MaskPutRect.x;
134 			MaskPutRect.Hgt = std::min<int32_t>(ystart + MatBuffPitch, ::Landscape.GetHeight()) - MaskPutRect.y;
135 		}
136 		// go through clipping rect
137 		const C4Real y0 = itofix(pClipRect->ty - MatBuffPitch/2);
138 		const C4Real x0 = itofix(pClipRect->tx - MatBuffPitch/2);
139 		iTy=pClipRect->y;
140 		int32_t w = pForObject->SolidMask.Wdt;
141 		int32_t h = pForObject->SolidMask.Hgt;
142 		int32_t mx0 = pForObject->SolidMask.x;
143 		int32_t my0 = pForObject->SolidMask.y;
144 		C4Real ya = y0 * Ma2;
145 		C4Real yb = y0 * Mb2;
146 		for (ycnt = 0; ycnt < pClipRect->Hgt; ycnt++)
147 		{
148 			iTx=pClipRect->x;
149 			int i = (ycnt + pClipRect->ty) * MatBuffPitch + pClipRect->tx;
150 			C4Real xa = x0 * Ma1;
151 			C4Real xb = x0 * Mb1;
152 			for (xcnt = 0; xcnt < pClipRect->Wdt; xcnt++)
153 			{
154 				// calc position in solidmask buffer
155 				int32_t iMx = fixtoi(xa + ya) + w / 2;
156 				int32_t iMy = fixtoi(xb + yb) + h / 2;
157 				// in bounds? and solidmask?
158 				if (iMx >= 0 && iMy >= 0 && iMx < w && iMy < h && pSolidMask->_GetPix(iMx+mx0, iMy+my0))
159 				{
160 					// is background mat to be stored?
161 					if (!MaskPut)
162 					{
163 						// get background pixel
164 						byPixel=::Landscape._GetPix(iTx,iTy);
165 						// store it. If MCVehic, also store in initial put, but won't be used in restore
166 						// do not overwrite current value in re-put issued by SolidMask-remover
167 						if (!IsSomeVehicle(byPixel) || RegularPut)
168 							pSolidMaskMatBuff[i + xcnt] = byPixel;
169 					}
170 					// set mask pix
171 					::Landscape.SetPix2(iTx, iTy, MaskMaterial, ::Landscape.Transparent);
172 				}
173 				else if (!MaskPut)
174 					// mark pix as unused in buf
175 					pSolidMaskMatBuff[i+xcnt] = MCVehic;
176 				xa += Ma1; xb += Mb1;
177 				++iTx;
178 			}
179 			ya += Ma2; yb += Mb2;
180 			++iTy;
181 		}
182 	}
183 	// Store mask put status
184 	MaskPut=true;
185 	// restore attached object positions if moved
186 	if (fRestoreAttachment && iAttachingObjectsCount)
187 	{
188 		C4Real dx = pForObject->GetFixedX() - MaskRemovalX;
189 		int32_t dy = pForObject->GetY() - MaskRemovalY;
190 		if (dx != Fix0 || dy != 0)
191 			for (int i = 0; i < iAttachingObjectsCount; ++i)
192 			{
193 				C4Object *pObj = ppAttachingObjects[i];
194 				if (pObj->IsMoveableBySolidMask(pForObject->GetSolidMaskPlane()))
195 					if (!pObj->Shape.ContactCheck(fixtoi(pObj->GetFixedX()+dx), fixtoi(pObj->GetFixedY()+dy)))
196 						if (pObj->iLastAttachMovementFrame != Game.FrameCounter)
197 						{
198 							pObj->iLastAttachMovementFrame = Game.FrameCounter;
199 							pObj->MovePosition(dx, itofix(dy));
200 						}
201 			}
202 		iAttachingObjectsCount = 0;
203 	}
204 
205 	if (fCauseInstability) CheckConsistency();
206 }
207 
GetDensity(int32_t x,int32_t y) const208 int32_t C4SolidMask::DensityProvider::GetDensity(int32_t x, int32_t y) const
209 {
210 	// outside SolidMask: free
211 	x -= rSolidMaskData.MaskPutRect.x;
212 	y -= rSolidMaskData.MaskPutRect.y;
213 	if (!Inside<int32_t>(x, 0, rSolidMaskData.MaskPutRect.Wdt-1)
214 	    || !Inside<int32_t>(y, 0, rSolidMaskData.MaskPutRect.Hgt-1))
215 		return 0;
216 	// check put mask. Easy for unrotated
217 	BYTE pix;
218 	if (!rSolidMaskData.MaskPutRotation)
219 	{
220 		CSurface8 *pSolidMask = rSolidMaskData.pForObject->Def->pSolidMask;
221 		if (!pSolidMask) return 0; // can't really happen
222 		pix=pSolidMask->_GetPix(x+rSolidMaskData.pForObject->SolidMask.x+rSolidMaskData.MaskPutRect.tx,
223 		                        y+rSolidMaskData.pForObject->SolidMask.y+rSolidMaskData.MaskPutRect.ty);
224 		if (pix == 0xff)
225 			return C4M_Vehicle;
226 		else
227 			return 0;
228 	}
229 	else
230 	{
231 		// Using put-buffer for rotated masks
232 		// for SolidMask-pixels not put because there was another SolidMask already, this will not return solid
233 		pix=*(rSolidMaskData.pSolidMaskMatBuff+(y+rSolidMaskData.MaskPutRect.ty)*rSolidMaskData.MatBuffPitch+rSolidMaskData.MaskPutRect.tx+x);
234 		if (IsSomeVehicle(pix))
235 			return 0;
236 		else
237 			return C4M_Vehicle;
238 	}
239 }
240 
Remove(bool fBackupAttachment)241 void C4SolidMask::Remove(bool fBackupAttachment)
242 {
243 	// If put, restore background pixels from buffer
244 
245 	// Not put
246 	if (!MaskPut || !pSolidMaskMatBuff) return;
247 
248 	CheckConsistency();
249 
250 	// reput background pixels
251 	for (int ycnt=0; ycnt<MaskPutRect.Hgt; ++ycnt)
252 	{
253 		BYTE *pPix=pSolidMaskMatBuff+(ycnt+MaskPutRect.ty)*MatBuffPitch+MaskPutRect.tx;
254 		for (int xcnt=0; xcnt<MaskPutRect.Wdt; ++xcnt,++pPix)
255 			// only if mask was used here
256 			if (*pPix != MCVehic)
257 			{
258 				// calc position in landscape
259 				int iTx=MaskPutRect.x+xcnt; int iTy=MaskPutRect.y+ycnt;
260 				// restore pixel here
261 				// The pPix-check ensures that only pixels that hads been overwritten by this SolidMask are restored
262 				// Non-SolidMask-pixels should not happen here, because all relevant landscape change routines should
263 				// temp remove SolidMasks before
264 				assert(IsSomeVehicle(::Landscape._GetPix(iTx,iTy)));
265 				if (IsSomeVehicle(::Landscape._GetPix(iTx, iTy)))
266 					::Landscape._SetPix2(iTx, iTy, *pPix, ::Landscape.Transparent);
267 				// Instability
268 				::Landscape.CheckInstabilityRange(iTx,iTy);
269 			}
270 	}
271 	// Mask not put flag
272 	MaskPut=false;
273 	// update surrounding masks in that range
274 	C4TargetRect ClipRect;
275 	for (C4SolidMask *pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
276 		if (pSolid->MaskPut) if (pSolid->MaskPutRect.Overlap(MaskPutRect))
277 			{
278 				// set clipping rect for all calls, since they may modify it
279 				ClipRect.Set(MaskPutRect.x, MaskPutRect.y, MaskPutRect.Wdt, MaskPutRect.Hgt, 0, 0);
280 				// doubled solidmask-pixels have just been removed in the clipped area!
281 				pSolid->MaskPut = false;
282 				// re-put the solidmask
283 				pSolid->Put(false, &ClipRect, false);
284 			}
285 
286 	// backup attachment if desired: Backup old pos and all objects that attach to or lie on the SolidMask
287 	if (fBackupAttachment)
288 	{
289 		MaskRemovalX = pForObject->GetFixedX();
290 		MaskRemovalY = pForObject->GetY();
291 		iAttachingObjectsCount = 0;
292 		// Search in area slightly larger than SolidMask because objects might have vertices slightly outside their shape
293 		C4LArea SolidArea(&::Objects.Sectors, MaskPutRect.x-1, MaskPutRect.y-4, MaskPutRect.Wdt+2, MaskPutRect.Hgt+2);
294 		C4LSector *pSct;
295 		for (C4ObjectList *pLst=SolidArea.FirstObjectShapes(&pSct); pLst; pLst=SolidArea.NextObjectShapes(pLst, &pSct))
296 			for (C4Object *pObj : *pLst)
297 				if (pObj && pObj != pForObject && pObj->IsMoveableBySolidMask(pForObject->GetSolidMaskPlane()) && !pObj->Shape.CheckContact(pObj->GetX(),pObj->GetY()))
298 				{
299 					// avoid duplicate that may be found due to sector overlaps
300 					bool has_dup = false;
301 					for (int32_t i_dup = 0; i_dup < iAttachingObjectsCount; ++i_dup)
302 						if (ppAttachingObjects[i_dup] == pObj)
303 						{
304 							has_dup = true;
305 							break;
306 						}
307 					if (has_dup) continue;
308 					// 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 :(
309 					int iVtx = 0;
310 					for (; iVtx < pObj->Shape.VtxNum; ++iVtx)
311 						if (pObj->Shape.GetVertexContact(iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->GetX(), pObj->GetY(), DensityProvider(pForObject, *this)))
312 							break;
313 					if (iVtx == pObj->Shape.VtxNum) continue; // no contact
314 					// contact: Add object to list
315 					if (iAttachingObjectsCapacity == iAttachingObjectsCount)
316 					{
317 						iAttachingObjectsCapacity += 4;
318 						C4Object **ppNewAttachingObjects = new C4Object *[iAttachingObjectsCapacity];
319 						if (iAttachingObjectsCount) memcpy(ppNewAttachingObjects, ppAttachingObjects, sizeof(C4Object *) * iAttachingObjectsCount);
320 						delete [] ppAttachingObjects;
321 						ppAttachingObjects = ppNewAttachingObjects;
322 					}
323 					ppAttachingObjects[iAttachingObjectsCount++] = pObj;
324 				}
325 	}
326 
327 	CheckConsistency();
328 }
329 
Draw(C4TargetFacet & cgo)330 void C4SolidMask::Draw(C4TargetFacet &cgo)
331 {
332 	// only if put
333 	if (!MaskPut) return;
334 	// set topface facet
335 	C4Facet fct; fct.Set(pForObject->GetGraphics()->GetBitmap(), pForObject->SolidMask.x, pForObject->SolidMask.y, pForObject->SolidMask.Wdt, pForObject->SolidMask.Hgt);
336 	// draw it
337 	if (MaskPutRotation)
338 		fct.DrawXR(cgo.Surface, pForObject->GetX()+pForObject->Shape.x+cgo.X-cgo.TargetX+pForObject->SolidMask.tx, pForObject->GetY()+pForObject->Shape.y+cgo.Y-cgo.TargetY+pForObject->SolidMask.ty, fct.Wdt, fct.Hgt, 0, 0, MaskPutRotation);
339 	else
340 		fct.DrawX(cgo.Surface, pForObject->GetX()+pForObject->Shape.x+cgo.X-cgo.TargetX+pForObject->SolidMask.tx, pForObject->GetY()+pForObject->Shape.y+cgo.Y-cgo.TargetY+pForObject->SolidMask.ty, fct.Wdt, fct.Hgt, 0, 0);
341 }
342 
343 
RemoveTemporary(C4Rect where)344 void C4SolidMask::RemoveTemporary(C4Rect where)
345 {
346 	if (!MaskPut || !pSolidMaskMatBuff) return;
347 	where.Intersect(MaskPutRect);
348 	// reput background pixels
349 	for (int y = where.y; y < where.y + where.Hgt; ++y)
350 	{
351 		for (int x = where.x; x < where.x + where.Wdt; ++x)
352 		{
353 			BYTE *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx;
354 			// only if mask was used here
355 			if (!IsSomeVehicle(*pPix)) //
356 			{
357 				// restore
358 				assert(IsSomeVehicle(::Landscape.GetPix(x,y)));
359 				::Landscape._SetPix2Tmp(x, y, *pPix, ::Landscape.Transparent);
360 			}
361 		}
362 	}
363 }
364 
PutTemporary(C4Rect where)365 void C4SolidMask::PutTemporary(C4Rect where)
366 {
367 	if (!MaskPut || !pSolidMaskMatBuff) return;
368 	where.Intersect(MaskPutRect);
369 	// reput vehicle pixels
370 	for (int y = where.y; y < where.y + where.Hgt; ++y)
371 	{
372 		for (int x = where.x; x < where.x + where.Wdt; ++x)
373 		{
374 			BYTE *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx;
375 			// only if mask was used here
376 			if (!IsSomeVehicle(*pPix))
377 			{
378 				// put
379 				assert(::Landscape.GetPix(x,y)==*pPix);
380 				::Landscape._SetPix2Tmp(x, y, MaskMaterial, ::Landscape.Transparent);
381 			}
382 		}
383 	}
384 }
385 
Repair(C4Rect where)386 void C4SolidMask::Repair(C4Rect where)
387 {
388 	if (!MaskPut || !pSolidMaskMatBuff) return;
389 	where.Intersect(MaskPutRect);
390 	// reput vehicle pixels
391 	for (int y = where.y; y < where.y + where.Hgt; ++y)
392 	{
393 		for (int x = where.x; x < where.x + where.Wdt; ++x)
394 		{
395 			BYTE *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx;
396 			// only if mask was used here
397 			if (!IsSomeVehicle(*pPix))
398 			{
399 				// record changed landscape in MatBuff
400 				*pPix = ::Landscape.GetPix(x,y);
401 				// put
402 				::Landscape.SetPix2(x, y, MaskMaterial, ::Landscape.Transparent);
403 			}
404 		}
405 	}
406 }
407 
C4SolidMask(C4Object * pForObject)408 C4SolidMask::C4SolidMask(C4Object *pForObject) : pForObject(pForObject)
409 {
410 	// zero fields
411 	MaskPut=false;
412 	MaskPutRotation=0;
413 	MaskRemovalX = Fix0;
414 	MaskRemovalY = 0;
415 	ppAttachingObjects=nullptr;
416 	iAttachingObjectsCount=iAttachingObjectsCapacity=0;
417 	MaskMaterial=MCVehic;
418 	// Update linked list
419 	Next = nullptr;
420 	Prev = Last;
421 	Last = this;
422 	if (Prev) Prev->Next = this;
423 	else First = this;
424 	// create mat buff to store the material replaced by the solid mask
425 	// the upper left corner is here the [objpos]+rot([shapexy]+[targetxy]+[realWH]/2)-maxWH/2
426 	MatBuffPitch = (int) sqrt(double(pForObject->SolidMask.Wdt * pForObject->SolidMask.Wdt + pForObject->SolidMask.Hgt * pForObject->SolidMask.Hgt))+1;
427 	if (!(pSolidMaskMatBuff= new BYTE [MatBuffPitch * MatBuffPitch] )) return;
428 	memset(pSolidMaskMatBuff, 0, MatBuffPitch * MatBuffPitch);
429 }
430 
~C4SolidMask()431 C4SolidMask::~C4SolidMask()
432 {
433 	Remove(false);
434 	// Update linked list
435 	if (Next) Next->Prev = Prev;
436 	if (Prev) Prev->Next = Next;
437 	if (First == this) First = Next;
438 	if (Last == this) Last = Prev;
439 	delete [] pSolidMaskMatBuff;
440 	delete [] ppAttachingObjects;
441 }
442 
RemoveSolidMasks()443 void C4SolidMask::RemoveSolidMasks()
444 {
445 	C4Rect SolidMaskRect(0,0,::Landscape.GetWidth(),::Landscape.GetHeight());
446 	C4SolidMask *pSolid;
447 	for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
448 	{
449 		pSolid->RemoveTemporary(SolidMaskRect);
450 	}
451 }
452 
PutSolidMasks()453 void C4SolidMask::PutSolidMasks()
454 {
455 	C4Rect SolidMaskRect(0,0,::Landscape.GetWidth(),::Landscape.GetHeight());
456 	C4SolidMask *pSolid;
457 	// Restore Solidmasks
458 	for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next)
459 	{
460 		pSolid->PutTemporary(SolidMaskRect);
461 	}
462 }
463 
464 C4SolidMask * C4SolidMask::First = nullptr;
465 C4SolidMask * C4SolidMask::Last = nullptr;
466 
467 
CheckConsistency()468 bool C4SolidMask::CheckConsistency()
469 {
470 	if (!SOLIDMASK_DEBUG)
471 		return true;
472 
473 	C4Rect SolidMaskRect(0,0,::Landscape.GetWidth(),::Landscape.GetHeight());
474 	C4SolidMask *pSolid;
475 	for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
476 	{
477 		pSolid->RemoveTemporary(SolidMaskRect);
478 	}
479 	assert(!::Landscape.GetMatCount(MVehic));
480 	// Restore Solidmasks
481 	for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next)
482 	{
483 		pSolid->PutTemporary(SolidMaskRect);
484 	}
485 	return true;
486 }
487 
LoadMaskFromFile(class C4Group & hGroup,const char * szFilename)488 CSurface8 *C4SolidMask::LoadMaskFromFile(class C4Group &hGroup, const char *szFilename)
489 {
490 	// Construct SolidMask surface from PNG bitmap:
491 	// All pixels that are more than 50% transparent are not solid
492 	CPNGFile png;
493 	StdBuf png_buf;
494 	if (!hGroup.LoadEntry(szFilename, &png_buf)) return nullptr; // error messages done by caller
495 	if (!png.Load((BYTE*)png_buf.getMData(), png_buf.getSize())) return nullptr;
496 	CSurface8 *result = new CSurface8(png.iWdt, png.iHgt);
497 	for (size_t y=0u; y<png.iHgt; ++y)
498 		for (size_t x=0u; x<png.iWdt; ++x)
499 			result->SetPix(x,y,((png.GetPix(x,y)>>24)<128) ? 0x00 : 0xff);
500 	return result;
501 }
502 
SetHalfVehicle(bool set)503 void C4SolidMask::SetHalfVehicle(bool set)
504 {
505 	MaskMaterial = set ? MCHalfVehic : MCVehic;
506 	// TODO: Redraw
507 }
508