1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk 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 /* Basic classes for rectangles and vertex outlines */
19 
20 #include "C4Include.h"
21 #include "object/C4Shape.h"
22 
23 #include "control/C4Record.h"
24 #include "game/C4Physics.h"
25 #include "landscape/C4Landscape.h"
26 #include "landscape/C4Material.h"
27 
AddVertex(int32_t iX,int32_t iY)28 bool C4Shape::AddVertex(int32_t iX, int32_t iY)
29 {
30 	if (VtxNum>=C4D_MaxVertex) return false;
31 	VtxX[VtxNum]=iX; VtxY[VtxNum]=iY;
32 	VtxNum++;
33 	return true;
34 }
35 
Default()36 void C4Shape::Default()
37 {
38 	InplaceReconstruct(this);
39 }
40 
Rotate(C4Real Angle,bool bUpdateVertices)41 void C4Shape::Rotate(C4Real Angle, bool bUpdateVertices)
42 {
43 	C4RCRotVtx rc;
44 	int32_t i = 0;
45 	if (Config.General.DebugRec)
46 	{
47 		rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt; rc.r=fixtoi(Angle);
48 		for (; i<4; ++i)
49 			{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
50 		AddDbgRec(RCT_RotVtx1, &rc, sizeof(rc));
51 	}
52 	int32_t cnt,nvtx,nvty,nwdt,nhgt;
53 
54 	C4Real mtx[4];
55 
56 	// Calculate rotation matrix
57 	mtx[0] = Cos(Angle); mtx[1] = -Sin(Angle);
58 	mtx[2] = -mtx[1];     mtx[3] = mtx[0];
59 
60 	if (bUpdateVertices)
61 	{
62 		// Rotate vertices
63 		for (cnt = 0; cnt < VtxNum; cnt++)
64 		{
65 			nvtx = fixtoi(mtx[0] * VtxX[cnt] + mtx[1] * VtxY[cnt]);
66 			nvty = fixtoi(mtx[2] * VtxX[cnt] + mtx[3] * VtxY[cnt]);
67 			VtxX[cnt] = nvtx; VtxY[cnt] = nvty;
68 		}
69 	}
70 
71 	// Enlarge Rect
72 	nvtx = fixtoi(mtx[0] * x + mtx[1] * y);
73 	nvty = fixtoi(mtx[2] * x + mtx[3] * y);
74 	if (mtx[0] > 0)
75 	{
76 		if (mtx[1] > 0)
77 		{
78 			nwdt = fixtoi(mtx[0] * Wdt + mtx[1] * Hgt);
79 			nhgt = fixtoi(mtx[1] * Wdt + mtx[0] * Hgt);
80 			x = nvtx;
81 			y = nvty - fixtoi(mtx[1] * Wdt);
82 		}
83 		else
84 		{
85 			nwdt = fixtoi(mtx[0] * Wdt - mtx[1] * Hgt);
86 			nhgt = fixtoi(- mtx[1] * Wdt + mtx[0] * Hgt);
87 			x = nvtx + fixtoi(mtx[1] * Hgt);
88 			y = nvty;
89 		}
90 	}
91 	else
92 	{
93 		if (mtx[1] > 0)
94 		{
95 			nwdt = fixtoi(- mtx[0] * Wdt + mtx[1] * Hgt);
96 			nhgt = fixtoi(mtx[1] * Wdt - mtx[0] * Hgt);
97 			x = nvtx + fixtoi(mtx[0] * Wdt);
98 			y = nvty - nhgt;
99 		}
100 		else
101 		{
102 			nwdt = fixtoi(- mtx[0] * Wdt - mtx[1] * Hgt);
103 			nhgt = fixtoi(- mtx[1] * Wdt - mtx[0] * Hgt);
104 			x = nvtx - nwdt;
105 			y = nvty + fixtoi(mtx[0] * Hgt);
106 		}
107 	}
108 	Wdt = nwdt;
109 	Hgt = nhgt;
110 	if (Config.General.DebugRec)
111 	{
112 		rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt;
113 		for (i=0; i<4; ++i)
114 			{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
115 		AddDbgRec(RCT_RotVtx2, &rc, sizeof(rc));
116 	}
117 }
118 
Stretch(int32_t iCon,bool bUpdateVertices)119 void C4Shape::Stretch(int32_t iCon, bool bUpdateVertices)
120 {
121 	int32_t cnt;
122 	x=x*iCon/FullCon;
123 	y=y*iCon/FullCon;
124 	Wdt=Wdt*iCon/FullCon;
125 	Hgt=Hgt*iCon/FullCon;
126 	if (bUpdateVertices)
127 		for (cnt=0; cnt<VtxNum; cnt++)
128 		{
129 			VtxX[cnt]=VtxX[cnt]*iCon/FullCon;
130 			VtxY[cnt]=VtxY[cnt]*iCon/FullCon;
131 		}
132 }
133 
Jolt(int32_t iCon,bool bUpdateVertices)134 void C4Shape::Jolt(int32_t iCon, bool bUpdateVertices)
135 {
136 	int32_t cnt;
137 	y=y*iCon/FullCon;
138 	Hgt=Hgt*iCon/FullCon;
139 	if (bUpdateVertices)
140 		for (cnt=0; cnt<VtxNum; cnt++)
141 			VtxY[cnt]=VtxY[cnt]*iCon/FullCon;
142 }
143 
GetVertexOutline(C4Rect & rRect)144 void C4Shape::GetVertexOutline(C4Rect &rRect)
145 {
146 	int32_t cnt;
147 	rRect.x=rRect.y=rRect.Wdt=rRect.Hgt=0;
148 	for (cnt=0; cnt<VtxNum; cnt++)
149 	{
150 		// Extend left
151 		if (VtxX[cnt]<rRect.x)
152 		{
153 			rRect.Wdt+=rRect.x-VtxX[cnt];
154 			rRect.x=VtxX[cnt];
155 		}
156 		// Extend right
157 		else if (VtxX[cnt]>rRect.x+rRect.Wdt)
158 			{ rRect.Wdt=VtxX[cnt]-rRect.x; }
159 
160 		// Extend up
161 		if (VtxY[cnt]<rRect.y)
162 		{
163 			rRect.Hgt+=rRect.y-VtxY[cnt];
164 			rRect.y=VtxY[cnt];
165 		}
166 		// Extend down
167 		else if (VtxY[cnt]>rRect.y+rRect.Hgt)
168 			{ rRect.Hgt=VtxY[cnt]-rRect.y; }
169 	}
170 
171 	rRect.Hgt+=rRect.y-y;
172 	rRect.y=y;
173 
174 }
175 
CheckTouchableMaterial(int32_t x,int32_t y,int32_t vtx_i,int32_t ydir,const C4DensityProvider & rDensityProvider)176 inline bool C4Shape::CheckTouchableMaterial(int32_t x, int32_t y, int32_t vtx_i, int32_t ydir, const C4DensityProvider &rDensityProvider) {
177 	return rDensityProvider.GetDensity(x,y) >= ContactDensity &&
178 	       ((ydir > 0 && !(CNAT_PhaseHalfVehicle & VtxCNAT[vtx_i])) || !IsMCHalfVehicle(::Landscape.GetPix(x,y)));
179 }
180 
181 // Adjust given position to one pixel before contact
182 // at vertices matching CNAT request.
Attach(int32_t & cx,int32_t & cy,BYTE cnat_pos)183 bool C4Shape::Attach(int32_t &cx, int32_t &cy, BYTE cnat_pos)
184 {
185 	// reset attached material
186 	AttachMat=MNone;
187 	int xcd = 0;
188 	int ycd = 0;
189 	// determine attachment direction
190 	switch (cnat_pos & (~CNAT_Flags))
191 	{
192 	case CNAT_Top:    ycd=-1; break;
193 	case CNAT_Bottom: ycd=+1; break;
194 	case CNAT_Left:   xcd=-1; break;
195 	case CNAT_Right:  xcd=+1; break;
196 	default: return false;
197 	}
198 	int testx = cx;
199 	int testy = cy;
200 	bool increase_distance = true;
201 	bool any_contact = false;
202 	// Find the nearest position that has at least one vertex adjacent to dense material
203 	// and no vertices in dense materials
204 	while (Abs(testx - cx) < AttachRange && Abs(testy - cy) < AttachRange)
205 	{
206 		bool found = false;
207 		for (int i = 0; i < VtxNum; ++i)
208 		{
209 			if (VtxCNAT[i] & cnat_pos)
210 			{
211 				// get new vertex pos
212 				int32_t ax = testx + VtxX[i], ay = testy + VtxY[i];
213 				if (CheckTouchableMaterial(ax, ay, i))
214 				{
215 					found = false;
216 					break;
217 				}
218 				// can attach here?
219 				if (CheckTouchableMaterial(ax + xcd, ay + ycd, i, ycd))
220 				{
221 					found = true;
222 					any_contact = true;
223 					// store attachment material
224 					AttachMat = GBackMat(ax + xcd, ay + ycd);
225 					// store absolute attachment position
226 					iAttachX = ax + xcd; iAttachY = ay + ycd;
227 					iAttachVtx = i;
228 				}
229 
230 			}
231 		}
232 		if (found)
233 		{
234 			cx = testx;
235 			cy = testy;
236 			return true;
237 		}
238 		// Try positions in order of distance from the origin,
239 		// and alternating the direction
240 		testx = cx - (testx - cx);
241 		testy = cy - (testy - cy);
242 		if (increase_distance)
243 		{
244 			testx += xcd;
245 			testy += ycd;
246 		}
247 		increase_distance = !increase_distance;
248 	}
249 	return any_contact;
250 }
251 
252 
LineConnect(int32_t tx,int32_t ty,int32_t cvtx,int32_t ld,int32_t oldx,int32_t oldy)253 bool C4Shape::LineConnect(int32_t tx, int32_t ty, int32_t cvtx, int32_t ld, int32_t oldx, int32_t oldy)
254 {
255 
256 	if (VtxNum<2) return false;
257 
258 	// No modification
259 	if ((VtxX[cvtx]==tx) && (VtxY[cvtx]==ty)) return true;
260 
261 	// Check new path
262 	int32_t ix,iy;
263 	if (PathFree(tx,ty,VtxX[cvtx+ld],VtxY[cvtx+ld],&ix,&iy))
264 	{
265 		// Okay, set vertex
266 		VtxX[cvtx]=tx; VtxY[cvtx]=ty;
267 		return true;
268 	}
269 	else
270 	{
271 		// Intersected, find bend vertex
272 		bool found = false;
273 		int32_t cix;
274 		int32_t ciy;
275 		for (int irange = 4; irange <= 12; irange += 4)
276 			for (cix = ix - irange / 2; cix <= ix + irange; cix += irange)
277 				for (ciy = iy - irange / 2; ciy <= iy + irange; ciy += irange)
278 				{
279 					if (PathFree(cix,ciy,tx,ty) && PathFree(cix,ciy,VtxX[cvtx+ld],VtxY[cvtx+ld]))
280 					{
281 						found = true;
282 						goto out;
283 					}
284 				}
285 out:
286 		if (!found)
287 		{
288 			// try bending directly at path the line took
289 			// allow going through vehicle in this case to allow lines through castles and elevator shafts
290 			cix = oldx;
291 			ciy = oldy;
292 			if (!PathFreeIgnoreVehicle(cix,ciy,tx,ty) || !PathFreeIgnoreVehicle(cix,ciy,VtxX[cvtx+ld],VtxY[cvtx+ld]))
293 				if (!PathFreeIgnoreVehicle(cix,ciy,tx,ty) || !PathFreeIgnoreVehicle(cix,ciy,VtxX[cvtx+ld],VtxY[cvtx+ld]))
294 					return false; // Found no bend vertex
295 		}
296 		// Insert bend vertex
297 		if (ld>0)
298 		{
299 			if (!InsertVertex(cvtx+1,cix,ciy)) return false;
300 		}
301 		else
302 		{
303 			if (!InsertVertex(cvtx,cix,ciy)) return false;
304 			cvtx++;
305 		}
306 		// Okay, set vertex
307 		VtxX[cvtx]=tx; VtxY[cvtx]=ty;
308 		return true;
309 	}
310 
311 	return false;
312 }
313 
InsertVertex(int32_t iPos,int32_t tx,int32_t ty)314 bool C4Shape::InsertVertex(int32_t iPos, int32_t tx, int32_t ty)
315 {
316 	if (VtxNum+1>C4D_MaxVertex) return false;
317 	if (iPos < 0 || iPos > VtxNum) return false;
318 	// Insert vertex before iPos
319 	for (int32_t cnt=VtxNum; cnt>iPos; cnt--)
320 		{ VtxX[cnt]=VtxX[cnt-1]; VtxY[cnt]=VtxY[cnt-1]; }
321 	VtxX[iPos]=tx; VtxY[iPos]=ty;
322 	VtxNum++;
323 	return true;
324 }
325 
RemoveVertex(int32_t iPos)326 bool C4Shape::RemoveVertex(int32_t iPos)
327 {
328 	if (!Inside<int32_t>(iPos,0,VtxNum-1)) return false;
329 	for (int32_t cnt=iPos; cnt+1<VtxNum; cnt++)
330 		{ VtxX[cnt]=VtxX[cnt+1]; VtxY[cnt]=VtxY[cnt+1]; }
331 	VtxNum--;
332 	return true;
333 }
334 
CheckContact(int32_t cx,int32_t cy)335 bool C4Shape::CheckContact(int32_t cx, int32_t cy)
336 {
337 	// Check all vertices at given object position.
338 	// Return true on any contact.
339 
340 
341 	for (int32_t cvtx=0; cvtx<VtxNum; cvtx++)
342 		if (!(VtxCNAT[cvtx] & CNAT_NoCollision))
343 			if (CheckTouchableMaterial(cx+VtxX[cvtx],cy+VtxY[cvtx], cvtx))
344 				return true;
345 
346 
347 	return false;
348 }
349 
ContactCheck(int32_t cx,int32_t cy,uint32_t * border_hack_contacts,bool collide_halfvehic)350 bool C4Shape::ContactCheck(int32_t cx, int32_t cy, uint32_t *border_hack_contacts, bool collide_halfvehic)
351 {
352 	// Check all vertices at given object position.
353 	// Set ContactCNAT and ContactCount.
354 	// Set VtxContactCNAT and VtxContactMat.
355 	// Return true on any contact.
356 
357 
358 	ContactCNAT=CNAT_None;
359 	ContactCount=0;
360 
361 	for (int32_t cvtx=0; cvtx<VtxNum; cvtx++)
362 
363 		// Ignore vertex if collision has been flagged out
364 		if (!(VtxCNAT[cvtx] & CNAT_NoCollision))
365 
366 		{
367 			VtxContactCNAT[cvtx]=CNAT_None;
368 			int32_t x = cx+VtxX[cvtx];
369 			int32_t y = cy+VtxY[cvtx];
370 			VtxContactMat[cvtx]=GBackMat(x,y);
371 
372 			if (CheckTouchableMaterial(x, y, cvtx, collide_halfvehic? 1:0))
373 			{
374 				ContactCNAT |= VtxCNAT[cvtx];
375 				VtxContactCNAT[cvtx]|=CNAT_Center;
376 				ContactCount++;
377 				// Vertex center contact, now check top,bottom,left,right
378 				if (CheckTouchableMaterial(x,y-1,cvtx, collide_halfvehic ? 1 : 0))
379 					VtxContactCNAT[cvtx]|=CNAT_Top;
380 				if (CheckTouchableMaterial(x,y+1,cvtx, collide_halfvehic ? 1 : 0))
381 					VtxContactCNAT[cvtx]|=CNAT_Bottom;
382 				if (CheckTouchableMaterial(x-1,y,cvtx, collide_halfvehic ? 1 : 0))
383 					VtxContactCNAT[cvtx]|=CNAT_Left;
384 				if (CheckTouchableMaterial(x+1,y,cvtx, collide_halfvehic ? 1 : 0))
385 					VtxContactCNAT[cvtx]|=CNAT_Right;
386 			}
387 			if (border_hack_contacts)
388 			{
389 				if (x == 0 && CheckTouchableMaterial(x-1, y, cvtx)) *border_hack_contacts |= CNAT_Left;
390 				else if (x == ::Landscape.GetWidth() && CheckTouchableMaterial(x+1, y, cvtx)) *border_hack_contacts |= CNAT_Right;
391 			}
392 		}
393 
394 
395 	return !!ContactCount;
396 }
397 
CheckScaleToWalk(int x,int y)398 bool C4Shape::CheckScaleToWalk(int x, int y)
399 {
400 	for (int32_t i = 0; i < VtxNum; i++)
401 	{
402 		if (VtxCNAT[i] & CNAT_NoCollision)
403 			continue;
404 		if (VtxCNAT[i] & CNAT_Bottom)
405 		{
406 			// no ground under the feet?
407 			if (CheckTouchableMaterial(x + VtxX[i], y + VtxY[i] + 1, i))
408 				return false;
409 		}
410 		else
411 		{
412 			// can climb with hands?
413 			if (CheckTouchableMaterial(x + VtxX[i] - 1, y + VtxY[i], i))
414 				return false;
415 			if (CheckTouchableMaterial(x + VtxX[i] + 1, y + VtxY[i], i))
416 				return false;
417 		}
418 	}
419 	return true;
420 }
421 
GetVertexX(int32_t iVertex)422 int32_t C4Shape::GetVertexX(int32_t iVertex)
423 {
424 	if (!Inside<int32_t>(iVertex,0,VtxNum-1)) return 0;
425 	return VtxX[iVertex];
426 }
427 
GetVertexY(int32_t iVertex)428 int32_t C4Shape::GetVertexY(int32_t iVertex)
429 {
430 	if (!Inside<int32_t>(iVertex,0,VtxNum-1)) return 0;
431 	return VtxY[iVertex];
432 }
433 
CopyFrom(C4Shape rFrom,bool bCpyVertices,bool fCopyVerticesFromSelf)434 void C4Shape::CopyFrom(C4Shape rFrom, bool bCpyVertices, bool fCopyVerticesFromSelf)
435 {
436 	if (bCpyVertices)
437 	{
438 		// truncate / copy vertex count
439 		VtxNum = (fCopyVerticesFromSelf ? std::min<int32_t>(VtxNum, C4D_VertexCpyPos) : rFrom.VtxNum);
440 		// restore vertices from back of own buffer (retaining count)
441 		int32_t iCopyPos = (fCopyVerticesFromSelf ? C4D_VertexCpyPos : 0);
442 		C4Shape &rVtxFrom = (fCopyVerticesFromSelf ? *this : rFrom);
443 		memcpy(VtxX, rVtxFrom.VtxX+iCopyPos, VtxNum*sizeof(*VtxX));
444 		memcpy(VtxY, rVtxFrom.VtxY+iCopyPos, VtxNum*sizeof(*VtxY));
445 		memcpy(VtxCNAT, rVtxFrom.VtxCNAT+iCopyPos, VtxNum*sizeof(*VtxCNAT));
446 		memcpy(VtxFriction, rVtxFrom.VtxFriction+iCopyPos, VtxNum*sizeof(*VtxFriction));
447 		memcpy(VtxContactCNAT, rVtxFrom.VtxContactCNAT+iCopyPos, VtxNum*sizeof(*VtxContactCNAT));
448 		memcpy(VtxContactMat, rVtxFrom.VtxContactMat+iCopyPos, VtxNum*sizeof(*VtxContactMat));
449 		// continue: copies other members
450 	}
451 	*((C4Rect *) this) = rFrom;
452 	AttachMat=rFrom.AttachMat;
453 	ContactCNAT=rFrom.ContactCNAT;
454 	ContactCount=rFrom.ContactCount;
455 }
456 
GetBottomVertex()457 int32_t C4Shape::GetBottomVertex()
458 {
459 	// return bottom-most vertex
460 	int32_t iMax = -1;
461 	for (int32_t i = 0; i < VtxNum; i++)
462 		if (VtxCNAT[i] & CNAT_Bottom)
463 			if (iMax == -1 || VtxY[i] < VtxY[iMax])
464 				iMax = i;
465 	return iMax;
466 }
467 
GetBottom()468 int C4Shape::GetBottom()
469 {
470 	int b = INT_MIN;
471 	for (int32_t i = 0; i < VtxNum; i++)
472 		if (~VtxCNAT[i] & CNAT_NoCollision)
473 			if (VtxY[i] > b)
474 				b = VtxY[i];
475 	if (b == INT_MIN)
476 		return y + Hgt;
477 	return b;
478 }
479 
480 C4DensityProvider DefaultDensityProvider;
481 
GetDensity(int32_t x,int32_t y) const482 int32_t C4DensityProvider::GetDensity(int32_t x, int32_t y) const
483 {
484 	// default density provider checks the landscape
485 	return GBackDensity(x,y);
486 }
487 
GetVertexContact(int32_t iVtx,DWORD dwCheckMask,int32_t tx,int32_t ty,const C4DensityProvider & rDensityProvider)488 int32_t C4Shape::GetVertexContact(int32_t iVtx, DWORD dwCheckMask, int32_t tx, int32_t ty, const C4DensityProvider &rDensityProvider)
489 {
490 	// default check mask
491 	if (!dwCheckMask) dwCheckMask = VtxCNAT[iVtx];
492 	// check vertex positions (vtx num not range-checked!)
493 	tx += VtxX[iVtx]; ty += VtxY[iVtx];
494 	int32_t iContact = 0;
495 	// check all directions for solid mat
496 	if (~VtxCNAT[iVtx] & CNAT_NoCollision)
497 	{
498 		if (dwCheckMask & CNAT_Center) if (CheckTouchableMaterial(tx, ty  , iVtx, 0, rDensityProvider)) iContact |= CNAT_Center;
499 		if (dwCheckMask & CNAT_Left)   if (CheckTouchableMaterial(tx-1, ty, iVtx, 0, rDensityProvider)) iContact |= CNAT_Left;
500 		if (dwCheckMask & CNAT_Right)  if (CheckTouchableMaterial(tx+1, ty, iVtx, 0, rDensityProvider)) iContact |= CNAT_Right;
501 		if (dwCheckMask & CNAT_Top)    if (CheckTouchableMaterial(tx, ty-1, iVtx, 0, rDensityProvider)) iContact |= CNAT_Top;
502 		if (dwCheckMask & CNAT_Bottom) if (CheckTouchableMaterial(tx, ty+1, iVtx, 1, rDensityProvider)) iContact |= CNAT_Bottom;
503 	}
504 	// return resulting bitmask
505 	return iContact;
506 }
507 
CreateOwnOriginalCopy(C4Shape & rFrom)508 void C4Shape::CreateOwnOriginalCopy(C4Shape &rFrom)
509 {
510 	// copy vertices from original buffer, including count
511 	VtxNum = std::min<int32_t>(rFrom.VtxNum, C4D_VertexCpyPos);
512 	memcpy(VtxX+C4D_VertexCpyPos, rFrom.VtxX, VtxNum*sizeof(*VtxX));
513 	memcpy(VtxY+C4D_VertexCpyPos, rFrom.VtxY, VtxNum*sizeof(*VtxY));
514 	memcpy(VtxCNAT+C4D_VertexCpyPos, rFrom.VtxCNAT, VtxNum*sizeof(*VtxCNAT));
515 	memcpy(VtxFriction+C4D_VertexCpyPos, rFrom.VtxFriction, VtxNum*sizeof(*VtxFriction));
516 	memcpy(VtxContactCNAT+C4D_VertexCpyPos, rFrom.VtxContactCNAT, VtxNum*sizeof(*VtxContactCNAT));
517 	memcpy(VtxContactMat+C4D_VertexCpyPos, rFrom.VtxContactMat, VtxNum*sizeof(*VtxContactMat));
518 }
519 
CompileFunc(StdCompiler * pComp,const C4Shape * default_shape)520 void C4Shape::CompileFunc(StdCompiler *pComp, const C4Shape *default_shape)
521 {
522 	const StdBitfieldEntry<int32_t> ContactDirections[] =
523 	{
524 
525 		{ "CNAT_None", CNAT_None },
526 		{ "CNAT_Left", CNAT_Left },
527 		{ "CNAT_Right", CNAT_Right },
528 		{ "CNAT_Top", CNAT_Top },
529 		{ "CNAT_Bottom", CNAT_Bottom },
530 		{ "CNAT_Center", CNAT_Center },
531 		{ "CNAT_MultiAttach", CNAT_MultiAttach },
532 		{ "CNAT_NoCollision", CNAT_NoCollision },
533 		{ "CNAT_PhaseHalfVehicle", CNAT_PhaseHalfVehicle },
534 
535 		{ nullptr, 0 }
536 	};
537 
538 	// a default shape is given in object compilation context only
539 	bool fRuntime = !!default_shape;
540 	C4Shape default_def_shape;
541 	if (!default_shape) default_shape = &default_def_shape;
542 	// Note: Compiled directly into "Object" and "DefCore"-categories, so beware of name clashes
543 	// (see C4Object::CompileFunc and C4Def::CompileFunc)
544 	pComp->Value(mkNamingAdapt( Wdt,                        "Width",              default_shape->Wdt));
545 	pComp->Value(mkNamingAdapt( Hgt,                        "Height",             default_shape->Hgt));
546 	pComp->Value(mkNamingAdapt( mkArrayAdaptDefArr(&x,2,&default_shape->x),               "Offset",             &default_shape->x));
547 	pComp->Value(mkNamingAdapt( VtxNum,                                                   "Vertices",           default_shape->VtxNum));
548 	pComp->Value(mkNamingAdapt( mkArrayAdaptDMA(VtxX, default_shape->VtxX),               "VertexX",            default_shape->VtxX));
549 	pComp->Value(mkNamingAdapt( mkArrayAdaptDMA(VtxY, default_shape->VtxY),               "VertexY",            default_shape->VtxY));
550 	pComp->Value(mkNamingAdapt( mkArrayAdaptDMAM(VtxCNAT, default_shape->VtxCNAT, [&](decltype(*VtxCNAT) &elem){ return mkBitfieldAdapt<int32_t>(elem, ContactDirections); }), "VertexCNAT", default_shape->VtxCNAT));
551 	pComp->Value(mkNamingAdapt( mkArrayAdaptDMA(VtxFriction, default_shape->VtxFriction), "VertexFriction",     default_shape->VtxFriction));
552 	pComp->Value(mkNamingAdapt( ContactDensity,             "ContactDensity",     default_shape->ContactDensity));
553 	if (fRuntime)
554 	{
555 		pComp->Value(mkNamingAdapt( iAttachX,                   "AttachX",            0                 ));
556 		pComp->Value(mkNamingAdapt( iAttachY,                   "AttachY",            0                 ));
557 		pComp->Value(mkNamingAdapt( iAttachVtx,                 "AttachVtx",          0                 ));
558 	}
559 }
560