forked from Mirrors/openclonk
790 lines
22 KiB
C++
790 lines
22 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, 2005, 2007 Matthes Bender
|
|
* Copyright (c) 2001-2007 Sven Eberhardt
|
|
* Copyright (c) 2003, 2005-2007 Peter Wortmann
|
|
* Copyright (c) 2006-2008 Günther Brammer
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
|
|
/* Basic classes for rectangles and vertex outlines */
|
|
|
|
#include <C4Include.h>
|
|
#include <C4Shape.h>
|
|
|
|
#ifndef BIG_C4INCLUDE
|
|
#include <C4Physics.h>
|
|
#include <C4Material.h>
|
|
#include <C4Landscape.h>
|
|
#endif
|
|
|
|
BOOL C4Shape::AddVertex(int32_t iX, int32_t iY)
|
|
{
|
|
if (VtxNum>=C4D_MaxVertex) return FALSE;
|
|
VtxX[VtxNum]=iX; VtxY[VtxNum]=iY;
|
|
VtxNum++;
|
|
return TRUE;
|
|
}
|
|
|
|
void C4Shape::Default()
|
|
{
|
|
ZeroMem(this,sizeof (C4Shape));
|
|
AttachMat=MNone;
|
|
ContactDensity=C4M_Solid;
|
|
}
|
|
|
|
C4Shape::C4Shape()
|
|
{
|
|
Default();
|
|
}
|
|
|
|
void C4Rect::Default()
|
|
{
|
|
x=y=Wdt=Hgt=0;
|
|
}
|
|
|
|
void C4Rect::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
pComp->Value(mkDefaultAdapt(x, 0)); pComp->Seperator();
|
|
pComp->Value(mkDefaultAdapt(y, 0)); pComp->Seperator();
|
|
pComp->Value(mkDefaultAdapt(Wdt, 0)); pComp->Seperator();
|
|
pComp->Value(mkDefaultAdapt(Hgt, 0));
|
|
}
|
|
|
|
void C4TargetRect::Default()
|
|
{
|
|
C4Rect::Default();
|
|
tx=ty=0;
|
|
}
|
|
|
|
void C4TargetRect::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iTX, int32_t iTY)
|
|
{
|
|
C4Rect::Set(iX,iY,iWdt,iHgt);
|
|
tx=iTX; ty=iTY;
|
|
}
|
|
|
|
bool C4TargetRect::ClipBy(C4TargetRect &rClip)
|
|
{
|
|
int32_t d;
|
|
// clip left
|
|
if ((d = x - rClip.x) < 0) { Wdt += d; x = rClip.x; } else tx += d;
|
|
// clip top
|
|
if ((d = y - rClip.y) < 0) { Hgt += d; y = rClip.y; } else ty += d;
|
|
// clip right
|
|
if ((d = (x+Wdt - rClip.x-rClip.Wdt)) > 0) Wdt -= d;
|
|
// clip bottom
|
|
if ((d = (y+Hgt - rClip.y-rClip.Hgt)) > 0) Hgt -= d;
|
|
// check validity
|
|
if (Wdt <= 0 || Hgt <= 0) return false;
|
|
// add target pos
|
|
tx += rClip.tx;
|
|
ty += rClip.ty;
|
|
// done
|
|
return true;
|
|
}
|
|
|
|
void C4TargetRect::Set(const C4TargetFacet &rSrc)
|
|
{
|
|
// copy members
|
|
x=rSrc.X; y=rSrc.Y; Wdt=rSrc.Wdt; Hgt=rSrc.Hgt; tx=rSrc.TargetX; ty=rSrc.TargetY;
|
|
}
|
|
|
|
void C4TargetRect::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
C4Rect::CompileFunc(pComp); pComp->Seperator();
|
|
pComp->Value(mkDefaultAdapt(tx,0)); pComp->Seperator();
|
|
pComp->Value(mkDefaultAdapt(ty,0));
|
|
}
|
|
|
|
void C4Rect::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
|
|
{
|
|
x=iX; y=iY; Wdt=iWdt; Hgt=iHgt;
|
|
}
|
|
|
|
BOOL C4Rect::Overlap(C4Rect &rTarget)
|
|
{
|
|
if (x+Wdt<=rTarget.x) return FALSE;
|
|
if (x>=rTarget.x+rTarget.Wdt) return FALSE;
|
|
if (y+Hgt<=rTarget.y) return FALSE;
|
|
if (y>=rTarget.y+rTarget.Hgt) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
void C4Rect::Intersect(const C4Rect &r2)
|
|
{
|
|
// Narrow bounds
|
|
if (r2.x > x)
|
|
if (r2.x + r2.Wdt < x + Wdt)
|
|
{ x = r2.x; Wdt = r2.Wdt; }
|
|
else
|
|
{ Wdt -= (r2.x - x); x = r2.x; }
|
|
else
|
|
if (r2.x + r2.Wdt < x + Wdt)
|
|
Wdt = r2.x + r2.Wdt - x;
|
|
if (r2.y > y)
|
|
if (r2.y + r2.Hgt < y + Hgt)
|
|
{ y = r2.y; Hgt = r2.Hgt; }
|
|
else
|
|
{ Hgt -= (r2.y - y); y = r2.y; }
|
|
else
|
|
if (r2.y + r2.Hgt < y + Hgt)
|
|
Hgt = r2.y + r2.Hgt - y;
|
|
// Degenerated? Will happen when the two rects don't overlap
|
|
if (Wdt < 0) Wdt = 0;
|
|
if (Hgt < 0) Hgt = 0;
|
|
}
|
|
|
|
bool C4Rect::IntersectsLine(int32_t iX, int32_t iY, int32_t iX2, int32_t iY2)
|
|
{
|
|
// Easy cases first
|
|
if(Contains(iX, iY)) return true;
|
|
if(Contains(iX2, iY2)) return true;
|
|
if(iX < x && iX2 < x) return false;
|
|
if(iY < y && iY2 < y) return false;
|
|
if(iX >= x+Wdt && iX2 >= x+Wdt) return false;
|
|
if(iY >= y+Hgt && iY2 >= y+Hgt) return false;
|
|
// check some special cases
|
|
if (iX == iX2 || iY == iY2) return true;
|
|
// Check intersection left/right
|
|
int32_t iXI, iYI;
|
|
iXI = (iX < x ? x : x+Wdt);
|
|
iYI = iY + (iY2 - iY) * (iXI - iX) / (iX2 - iX);
|
|
if(iYI >= y && iYI < y+Hgt) return true;
|
|
// Check intersection up/down
|
|
iYI = (iY < y ? y : y+Hgt);
|
|
iXI = iX + (iX2 - iX) * (iYI - iY) / (iY2 - iY);
|
|
return iXI >= x && iXI < x+Wdt;
|
|
}
|
|
|
|
void C4Rect::Add(const C4Rect &r2)
|
|
{
|
|
// Null? Don't do anything
|
|
if (!r2.Wdt || !r2.Hgt) return;
|
|
if (!Wdt || !Hgt)
|
|
{
|
|
*this = r2;
|
|
return;
|
|
}
|
|
// Expand bounds
|
|
if (r2.x < x)
|
|
if (r2.x + r2.Wdt > x + Wdt)
|
|
{ x = r2.x; Wdt = r2.Wdt; }
|
|
else
|
|
{ Wdt += (x - r2.x); x = r2.x; }
|
|
else
|
|
if (r2.x + r2.Wdt > x + Wdt)
|
|
Wdt = r2.x + r2.Wdt - x;
|
|
if (r2.y < y)
|
|
if (r2.y + r2.Hgt > y + Hgt)
|
|
{ y = r2.y; Hgt = r2.Hgt; }
|
|
else
|
|
{ Hgt += (y - r2.y); y = r2.y; }
|
|
else
|
|
if (r2.y + r2.Hgt > y + Hgt)
|
|
Hgt = r2.y + r2.Hgt - y;
|
|
}
|
|
|
|
void C4Shape::Clear()
|
|
{
|
|
ZeroMem(this, sizeof (C4Shape));
|
|
}
|
|
|
|
void C4Shape::Rotate(int32_t iAngle, bool bUpdateVertices)
|
|
{
|
|
#ifdef DEBUGREC
|
|
C4RCRotVtx rc;
|
|
rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt; rc.r=iAngle;
|
|
int32_t i = 0;
|
|
for (; i<4; ++i)
|
|
{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
|
|
AddDbgRec(RCT_RotVtx1, &rc, sizeof(rc));
|
|
#endif
|
|
int32_t cnt,nvtx,nvty,rdia;
|
|
|
|
//int32_t *vtx=VtxX;
|
|
//int32_t *vty=VtxY;
|
|
FIXED mtx[4];
|
|
FIXED fAngle = itofix(iAngle);
|
|
|
|
if (bUpdateVertices)
|
|
{
|
|
|
|
// Calculate rotation matrix
|
|
mtx[0]=Cos(fAngle); mtx[1]=-Sin(fAngle);
|
|
mtx[2]=-mtx[1]; mtx[3]= mtx[0];
|
|
// Rotate vertices
|
|
for (cnt=0; cnt<VtxNum; cnt++)
|
|
{
|
|
//nvtx= (int32_t) ( mtx[0]*vtx[cnt] + mtx[1]*vty[cnt] );
|
|
//nvty= (int32_t) ( mtx[2]*vtx[cnt] + mtx[3]*vty[cnt] );
|
|
nvtx = fixtoi(mtx[0]*VtxX[cnt] + mtx[1]*VtxY[cnt]);
|
|
nvty = fixtoi(mtx[2]*VtxX[cnt] + mtx[3]*VtxY[cnt]);
|
|
VtxX[cnt]=nvtx; VtxY[cnt]=nvty;
|
|
}
|
|
|
|
/* This is freaking nuts. I used the int32_t* to shortcut the
|
|
two int32_t arrays Shape.Vtx_[]. Without modifications to
|
|
this code, after rotation the x-values of vertex 2 and 4
|
|
are screwed to that of vertex 0. Direct use of the array
|
|
variables instead of the pointers helped. Later in
|
|
development, again without modification to this code, the
|
|
same error occured again. I moved back to pointer array
|
|
shortcut and it worked again. ?!
|
|
|
|
The error occurs after the C4DefCore structure has
|
|
changed. It must have something to do with struct
|
|
member alignment. But why does pointer usage vs. array
|
|
index make a difference?
|
|
*/
|
|
|
|
}
|
|
|
|
// Enlarge Rect
|
|
rdia= (int32_t) sqrt(double(x*x+y*y)) + 2;
|
|
x=-rdia;
|
|
y=-rdia;
|
|
Wdt=2*rdia;
|
|
Hgt=2*rdia;
|
|
#ifdef DEBUGREC
|
|
rc.x=x; rc.y=y; rc.wdt=Wdt; rc.hgt=Hgt;
|
|
for (i=0; i<4; ++i)
|
|
{ rc.VtxX[i]=VtxX[i]; rc.VtxY[i]=VtxY[i]; }
|
|
AddDbgRec(RCT_RotVtx2, &rc, sizeof(rc));
|
|
#endif
|
|
}
|
|
|
|
void C4Shape::Stretch(int32_t iCon, bool bUpdateVertices)
|
|
{
|
|
int32_t cnt;
|
|
x=x*iCon/FullCon;
|
|
y=y*iCon/FullCon;
|
|
Wdt=Wdt*iCon/FullCon;
|
|
Hgt=Hgt*iCon/FullCon;
|
|
FireTop=FireTop*iCon/FullCon;
|
|
if (bUpdateVertices)
|
|
for (cnt=0; cnt<VtxNum; cnt++)
|
|
{
|
|
VtxX[cnt]=VtxX[cnt]*iCon/FullCon;
|
|
VtxY[cnt]=VtxY[cnt]*iCon/FullCon;
|
|
}
|
|
}
|
|
|
|
void C4Shape::Jolt(int32_t iCon, bool bUpdateVertices)
|
|
{
|
|
int32_t cnt;
|
|
y=y*iCon/FullCon;
|
|
Hgt=Hgt*iCon/FullCon;
|
|
FireTop=FireTop*iCon/FullCon;
|
|
if (bUpdateVertices)
|
|
for (cnt=0; cnt<VtxNum; cnt++)
|
|
VtxY[cnt]=VtxY[cnt]*iCon/FullCon;
|
|
}
|
|
|
|
void C4Shape::GetVertexOutline(C4Rect &rRect)
|
|
{
|
|
int32_t cnt;
|
|
rRect.x=rRect.y=rRect.Wdt=rRect.Hgt=0;
|
|
for (cnt=0; cnt<VtxNum; cnt++)
|
|
{
|
|
// Extend left
|
|
if (VtxX[cnt]<rRect.x)
|
|
{
|
|
rRect.Wdt+=rRect.x-VtxX[cnt];
|
|
rRect.x=VtxX[cnt];
|
|
}
|
|
// Extend right
|
|
else if (VtxX[cnt]>rRect.x+rRect.Wdt)
|
|
{ rRect.Wdt=VtxX[cnt]-rRect.x; }
|
|
|
|
// Extend up
|
|
if (VtxY[cnt]<rRect.y)
|
|
{
|
|
rRect.Hgt+=rRect.y-VtxY[cnt];
|
|
rRect.y=VtxY[cnt];
|
|
}
|
|
// Extend down
|
|
else if (VtxY[cnt]>rRect.y+rRect.Hgt)
|
|
{ rRect.Hgt=VtxY[cnt]-rRect.y; }
|
|
}
|
|
|
|
rRect.Hgt+=rRect.y-y;
|
|
rRect.y=y;
|
|
|
|
}
|
|
|
|
|
|
BOOL C4Shape::Attach(int32_t &cx, int32_t &cy, BYTE cnat_pos)
|
|
{
|
|
// Adjust given position to one pixel before contact
|
|
// at vertices matching CNAT request.
|
|
|
|
BOOL fAttached=FALSE;
|
|
|
|
#ifdef C4ENGINE
|
|
|
|
int32_t vtx,xcnt,ycnt,xcrng,ycrng,xcd,ycd;
|
|
BYTE cpix;
|
|
|
|
// reset attached material
|
|
AttachMat=MNone;
|
|
|
|
// New attachment behaviour in CE:
|
|
// Before, attachment was done by searching through all vertices,
|
|
// and doing attachment to any vertex with a matching CNAT.
|
|
// While this worked well for normal Clonk attachment, it caused nonsense
|
|
// behaviour if multiple vertices matched the same CNAT. In effect, attachment
|
|
// was then done to the last vertex only, usually stucking the object sooner
|
|
// or later.
|
|
// For instance, the scaling procedure of regular Clonks uses two CNAT_Left-
|
|
// vertices (shoulder+belly), which "block" each other in situations like
|
|
// scaling up battlements of towers. That way, the 2px-overhang of the
|
|
// battlement is sufficient for keeping out scaling Clonks. The drawback is
|
|
// that sometimes Clonks get stuck scaling in very sharp edges or single
|
|
// floating material pixels; occuring quite often in Caverace, or maps where
|
|
// you blast Granite and many single pixels remain.
|
|
//
|
|
// Until a better solution for designing battlements is found, the old-style
|
|
// behaviour will be used for Clonks. Both code variants should behave equally
|
|
// for objects with only one matching vertex to cnat_pos.
|
|
if (!(cnat_pos & CNAT_MultiAttach))
|
|
{
|
|
// old-style attachment
|
|
for (vtx=0; vtx<VtxNum; vtx++)
|
|
if (VtxCNAT[vtx] & cnat_pos)
|
|
{
|
|
xcd=ycd=0;
|
|
switch (cnat_pos & (~CNAT_Flags))
|
|
{
|
|
case CNAT_Top: ycd=-1; break;
|
|
case CNAT_Bottom: ycd=+1; break;
|
|
case CNAT_Left: xcd=-1; break;
|
|
case CNAT_Right: xcd=+1; break;
|
|
}
|
|
xcrng=AttachRange*xcd*(-1); ycrng=AttachRange*ycd*(-1);
|
|
for (xcnt=xcrng,ycnt=ycrng; (xcnt!=-xcrng) || (ycnt!=-ycrng); xcnt+=xcd,ycnt+=ycd)
|
|
{
|
|
int32_t ax=cx+VtxX[vtx]+xcnt+xcd, ay=cy+VtxY[vtx]+ycnt+ycd;
|
|
if (GBackDensity(ax,ay) >= ContactDensity)
|
|
{
|
|
cpix=GBackPix(ax,ay);
|
|
AttachMat=PixCol2Mat(cpix);
|
|
iAttachX=ax; iAttachY=ay;
|
|
iAttachVtx=vtx;
|
|
cx+=xcnt; cy+=ycnt;
|
|
fAttached=1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // CNAT_MultiAttach
|
|
{
|
|
// new-style attachment
|
|
// determine attachment direction
|
|
xcd=ycd=0;
|
|
switch (cnat_pos & (~CNAT_Flags))
|
|
{
|
|
case CNAT_Top: ycd=-1; break;
|
|
case CNAT_Bottom: ycd=+1; break;
|
|
case CNAT_Left: xcd=-1; break;
|
|
case CNAT_Right: xcd=+1; break;
|
|
}
|
|
// check within attachment range
|
|
xcrng=AttachRange*xcd*(-1); ycrng=AttachRange*ycd*(-1);
|
|
for (xcnt=xcrng,ycnt=ycrng; (xcnt!=-xcrng) || (ycnt!=-ycrng); xcnt+=xcd,ycnt+=ycd)
|
|
// check all vertices with matching CNAT
|
|
for (vtx=0; vtx<VtxNum; vtx++)
|
|
if (VtxCNAT[vtx] & cnat_pos)
|
|
{
|
|
// get new vertex pos
|
|
int32_t ax=cx+VtxX[vtx]+xcnt+xcd, ay=cy+VtxY[vtx]+ycnt+ycd;
|
|
// can attach here?
|
|
cpix=GBackPix(ax,ay);
|
|
if (MatDensity(PixCol2Mat(cpix)) >= ContactDensity)
|
|
{
|
|
// store attachment material
|
|
AttachMat=PixCol2Mat(cpix);
|
|
// store absolute attachment position
|
|
iAttachX=ax; iAttachY=ay;
|
|
iAttachVtx=vtx;
|
|
// move position here
|
|
cx+=xcnt; cy+=ycnt;
|
|
// mark attachment
|
|
fAttached=1;
|
|
// break both looops
|
|
xcnt=-xcrng-xcd; ycnt=-ycrng-ycd;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return fAttached;
|
|
}
|
|
|
|
|
|
BOOL C4Shape::LineConnect(int32_t tx, int32_t ty, int32_t cvtx, int32_t ld, int32_t oldx, int32_t oldy)
|
|
{
|
|
#ifdef C4ENGINE
|
|
|
|
if (VtxNum<2) return FALSE;
|
|
|
|
// No modification
|
|
if ((VtxX[cvtx]==tx) && (VtxY[cvtx]==ty)) return TRUE;
|
|
|
|
// Check new path
|
|
int32_t ix,iy;
|
|
if (PathFree(tx,ty,VtxX[cvtx+ld],VtxY[cvtx+ld],&ix,&iy))
|
|
{
|
|
// Okay, set vertex
|
|
VtxX[cvtx]=tx; VtxY[cvtx]=ty;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Intersected, find bend vertex
|
|
bool found = false;
|
|
int32_t cix;
|
|
int32_t ciy;
|
|
for (int irange = 4; irange <= 12; irange += 4)
|
|
for (cix = ix - irange / 2; cix <= ix + irange; cix += irange)
|
|
for (ciy = iy - irange / 2; ciy <= iy + irange; ciy += irange)
|
|
{
|
|
if (PathFree(cix,ciy,tx,ty) && PathFree(cix,ciy,VtxX[cvtx+ld],VtxY[cvtx+ld]))
|
|
{
|
|
found = true;
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
if (!found)
|
|
{
|
|
// try bending directly at path the line took
|
|
// allow going through vehicle in this case to allow lines through castles and elevator shafts
|
|
cix = oldx;
|
|
ciy = oldy;
|
|
if (!PathFreeIgnoreVehicle(cix,ciy,tx,ty) || !PathFreeIgnoreVehicle(cix,ciy,VtxX[cvtx+ld],VtxY[cvtx+ld]))
|
|
if (!PathFreeIgnoreVehicle(cix,ciy,tx,ty) || !PathFreeIgnoreVehicle(cix,ciy,VtxX[cvtx+ld],VtxY[cvtx+ld]))
|
|
return FALSE; // Found no bend vertex
|
|
}
|
|
// Insert bend vertex
|
|
if (ld>0)
|
|
{
|
|
if (!InsertVertex(cvtx+1,cix,ciy)) return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!InsertVertex(cvtx,cix,ciy)) return FALSE;
|
|
cvtx++;
|
|
}
|
|
// Okay, set vertex
|
|
VtxX[cvtx]=tx; VtxY[cvtx]=ty;
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL C4Shape::InsertVertex(int32_t iPos, int32_t tx, int32_t ty)
|
|
{
|
|
if (VtxNum+1>C4D_MaxVertex) return FALSE;
|
|
// Insert vertex before iPos
|
|
for (int32_t cnt=VtxNum; cnt>iPos; cnt--)
|
|
{ VtxX[cnt]=VtxX[cnt-1]; VtxY[cnt]=VtxY[cnt-1]; }
|
|
VtxX[iPos]=tx; VtxY[iPos]=ty;
|
|
VtxNum++;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL C4Shape::RemoveVertex(int32_t iPos)
|
|
{
|
|
if (!Inside<int32_t>(iPos,0,VtxNum-1)) return FALSE;
|
|
for (int32_t cnt=iPos; cnt+1<VtxNum; cnt++)
|
|
{ VtxX[cnt]=VtxX[cnt+1]; VtxY[cnt]=VtxY[cnt+1]; }
|
|
VtxNum--;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL C4Shape::CheckContact(int32_t cx, int32_t cy)
|
|
{
|
|
// Check all vertices at given object position.
|
|
// Return TRUE on any contact.
|
|
|
|
#ifdef C4ENGINE
|
|
|
|
for (int32_t cvtx=0; cvtx<VtxNum; cvtx++)
|
|
if (!(VtxCNAT[cvtx] & CNAT_NoCollision))
|
|
if (GBackDensity(cx+VtxX[cvtx],cy+VtxY[cvtx]) >= ContactDensity)
|
|
return TRUE;
|
|
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL C4Shape::ContactCheck(int32_t cx, int32_t cy)
|
|
{
|
|
// Check all vertices at given object position.
|
|
// Set ContactCNAT and ContactCount.
|
|
// Set VtxContactCNAT and VtxContactMat.
|
|
// Return TRUE on any contact.
|
|
|
|
#ifdef C4ENGINE
|
|
|
|
ContactCNAT=CNAT_None;
|
|
ContactCount=0;
|
|
|
|
for (int32_t cvtx=0; cvtx<VtxNum; cvtx++)
|
|
|
|
// Ignore vertex if collision has been flagged out
|
|
if (!(VtxCNAT[cvtx] & CNAT_NoCollision))
|
|
|
|
{
|
|
|
|
VtxContactCNAT[cvtx]=CNAT_None;
|
|
VtxContactMat[cvtx]=GBackMat(cx+VtxX[cvtx],cy+VtxY[cvtx]);
|
|
|
|
if (GBackDensity(cx+VtxX[cvtx],cy+VtxY[cvtx]) >= ContactDensity)
|
|
{
|
|
ContactCNAT |= VtxCNAT[cvtx];
|
|
VtxContactCNAT[cvtx]|=CNAT_Center;
|
|
ContactCount++;
|
|
// Vertex center contact, now check top,bottom,left,right
|
|
if (GBackDensity(cx+VtxX[cvtx],cy+VtxY[cvtx]-1) >= ContactDensity)
|
|
VtxContactCNAT[cvtx]|=CNAT_Top;
|
|
if (GBackDensity(cx+VtxX[cvtx],cy+VtxY[cvtx]+1) >= ContactDensity)
|
|
VtxContactCNAT[cvtx]|=CNAT_Bottom;
|
|
if (GBackDensity(cx+VtxX[cvtx]-1,cy+VtxY[cvtx]) >= ContactDensity)
|
|
VtxContactCNAT[cvtx]|=CNAT_Left;
|
|
if (GBackDensity(cx+VtxX[cvtx]+1,cy+VtxY[cvtx]) >= ContactDensity)
|
|
VtxContactCNAT[cvtx]|=CNAT_Right;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return ContactCount;
|
|
}
|
|
|
|
int32_t C4Shape::GetVertexX(int32_t iVertex)
|
|
{
|
|
if (!Inside<int32_t>(iVertex,0,VtxNum-1)) return 0;
|
|
return VtxX[iVertex];
|
|
}
|
|
|
|
int32_t C4Shape::GetVertexY(int32_t iVertex)
|
|
{
|
|
if (!Inside<int32_t>(iVertex,0,VtxNum-1)) return 0;
|
|
return VtxY[iVertex];
|
|
}
|
|
|
|
void C4Shape::CopyFrom(C4Shape rFrom, bool bCpyVertices, bool fCopyVerticesFromSelf)
|
|
{
|
|
if (bCpyVertices)
|
|
{
|
|
// truncate / copy vertex count
|
|
VtxNum = (fCopyVerticesFromSelf ? Min<int32_t>(VtxNum, C4D_VertexCpyPos) : rFrom.VtxNum);
|
|
// restore vertices from back of own buffer (retaining count)
|
|
int32_t iCopyPos = (fCopyVerticesFromSelf ? C4D_VertexCpyPos : 0);
|
|
C4Shape &rVtxFrom = (fCopyVerticesFromSelf ? *this : rFrom);
|
|
memcpy(VtxX, rVtxFrom.VtxX+iCopyPos, VtxNum*sizeof(*VtxX));
|
|
memcpy(VtxY, rVtxFrom.VtxY+iCopyPos, VtxNum*sizeof(*VtxY));
|
|
memcpy(VtxCNAT, rVtxFrom.VtxCNAT+iCopyPos, VtxNum*sizeof(*VtxCNAT));
|
|
memcpy(VtxFriction, rVtxFrom.VtxFriction+iCopyPos, VtxNum*sizeof(*VtxFriction));
|
|
memcpy(VtxContactCNAT, rVtxFrom.VtxContactCNAT+iCopyPos, VtxNum*sizeof(*VtxContactCNAT));
|
|
memcpy(VtxContactMat, rVtxFrom.VtxContactMat+iCopyPos, VtxNum*sizeof(*VtxContactMat));
|
|
// continue: copies other members
|
|
}
|
|
*((C4Rect *) this) = rFrom;
|
|
AttachMat=rFrom.AttachMat;
|
|
ContactCNAT=rFrom.ContactCNAT;
|
|
ContactCount=rFrom.ContactCount;
|
|
FireTop=rFrom.FireTop;
|
|
}
|
|
|
|
int32_t C4Shape::GetBottomVertex()
|
|
{
|
|
// return bottom-most vertex
|
|
int32_t iMax = -1;
|
|
for(int32_t i = 0; i < VtxNum; i++)
|
|
if(VtxCNAT[i] & CNAT_Bottom)
|
|
if(iMax == -1 || VtxY[i] < VtxY[iMax])
|
|
iMax = i;
|
|
return iMax;
|
|
}
|
|
|
|
C4DensityProvider DefaultDensityProvider;
|
|
|
|
int32_t C4DensityProvider::GetDensity(int32_t x, int32_t y) const
|
|
{
|
|
#ifdef C4ENGINE
|
|
// default density provider checks the landscape
|
|
return GBackDensity(x,y);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int32_t C4Shape::GetVertexContact(int32_t iVtx, DWORD dwCheckMask, int32_t tx, int32_t ty, const C4DensityProvider &rDensityProvider)
|
|
{
|
|
// default check mask
|
|
if (!dwCheckMask) dwCheckMask = VtxCNAT[iVtx];
|
|
// check vertex positions (vtx num not range-checked!)
|
|
tx += VtxX[iVtx]; ty += VtxY[iVtx];
|
|
int32_t iContact = 0;
|
|
#ifdef C4ENGINE
|
|
// check all directions for solid mat
|
|
if (~VtxCNAT[iVtx] & CNAT_NoCollision)
|
|
{
|
|
if (dwCheckMask & CNAT_Center) if (rDensityProvider.GetDensity(tx, ty) >= ContactDensity) iContact |= CNAT_Center;
|
|
if (dwCheckMask & CNAT_Left) if (rDensityProvider.GetDensity(tx-1, ty) >= ContactDensity) iContact |= CNAT_Left;
|
|
if (dwCheckMask & CNAT_Right) if (rDensityProvider.GetDensity(tx+1, ty) >= ContactDensity) iContact |= CNAT_Right;
|
|
if (dwCheckMask & CNAT_Top) if (rDensityProvider.GetDensity(tx, ty-1) >= ContactDensity) iContact |= CNAT_Top;
|
|
if (dwCheckMask & CNAT_Bottom) if (rDensityProvider.GetDensity(tx, ty+1) >= ContactDensity) iContact |= CNAT_Bottom;
|
|
}
|
|
#endif
|
|
// return resulting bitmask
|
|
return iContact;
|
|
}
|
|
|
|
void C4Shape::CreateOwnOriginalCopy(C4Shape &rFrom)
|
|
{
|
|
// copy vertices from original buffer, including count
|
|
VtxNum = Min<int32_t>(rFrom.VtxNum, C4D_VertexCpyPos);
|
|
memcpy(VtxX+C4D_VertexCpyPos, rFrom.VtxX, VtxNum*sizeof(*VtxX));
|
|
memcpy(VtxY+C4D_VertexCpyPos, rFrom.VtxY, VtxNum*sizeof(*VtxY));
|
|
memcpy(VtxCNAT+C4D_VertexCpyPos, rFrom.VtxCNAT, VtxNum*sizeof(*VtxCNAT));
|
|
memcpy(VtxFriction+C4D_VertexCpyPos, rFrom.VtxFriction, VtxNum*sizeof(*VtxFriction));
|
|
memcpy(VtxContactCNAT+C4D_VertexCpyPos, rFrom.VtxContactCNAT, VtxNum*sizeof(*VtxContactCNAT));
|
|
memcpy(VtxContactMat+C4D_VertexCpyPos, rFrom.VtxContactMat, VtxNum*sizeof(*VtxContactMat));
|
|
}
|
|
|
|
void C4Shape::CompileFunc(StdCompiler *pComp, bool fRuntime)
|
|
{
|
|
// Note: Compiled directly into "Object" and "DefCore"-categories, so beware of name clashes
|
|
// (see C4Object::CompileFunc and C4DefCore::CompileFunc)
|
|
pComp->Value(mkNamingAdapt( Wdt, "Width", 0 ));
|
|
pComp->Value(mkNamingAdapt( Hgt, "Height", 0 ));
|
|
pComp->Value(mkNamingAdapt( mkArrayAdapt(&x,2,0), "Offset" ));
|
|
pComp->Value(mkNamingAdapt( VtxNum, "Vertices", 0 ));
|
|
pComp->Value(mkNamingAdapt( toC4CArr(VtxX), "VertexX" ));
|
|
pComp->Value(mkNamingAdapt( toC4CArr(VtxY), "VertexY" ));
|
|
pComp->Value(mkNamingAdapt( toC4CArr(VtxCNAT), "VertexCNAT" ));
|
|
pComp->Value(mkNamingAdapt( toC4CArr(VtxFriction), "VertexFriction" ));
|
|
pComp->Value(mkNamingAdapt( ContactDensity, "ContactDensity", C4M_Solid ));
|
|
pComp->Value(mkNamingAdapt( FireTop, "FireTop", 0 ));
|
|
if(fRuntime)
|
|
{
|
|
pComp->Value(mkNamingAdapt( iAttachX, "AttachX", 0 ));
|
|
pComp->Value(mkNamingAdapt( iAttachY, "AttachY", 0 ));
|
|
pComp->Value(mkNamingAdapt( iAttachVtx, "AttachVtx", 0 ));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ---- C4RectList ----
|
|
|
|
#ifdef C4ENGINE
|
|
|
|
void C4RectList::ClipByRect(const C4Rect &rClip)
|
|
{
|
|
// split up all rectangles
|
|
for (int32_t i = 0; i < GetCount(); ++i)
|
|
{
|
|
C4Rect *pTarget = &Get(i);
|
|
// any overlap?
|
|
if (rClip.x+rClip.Wdt <= pTarget->x) continue;
|
|
if (rClip.y+rClip.Hgt <= pTarget->y) continue;
|
|
if (rClip.x >= pTarget->x+pTarget->Wdt) continue;
|
|
if (rClip.y >= pTarget->y+pTarget->Hgt) continue;
|
|
// okay; split up rectangle
|
|
// first split will just reduce the target rectangle size
|
|
// if more splits are done, additional rectangles need to be added
|
|
int32_t iSplitCount = 0, iOver; C4Rect rcThis(*pTarget);
|
|
// clipped by right side
|
|
if ((iOver=rcThis.x+rcThis.Wdt-rClip.x-rClip.Wdt)>0)
|
|
{
|
|
pTarget->x += pTarget->Wdt - iOver; pTarget->Wdt = iOver; rcThis.Wdt -= iOver;
|
|
++iSplitCount;
|
|
}
|
|
// clipped by obttom side
|
|
if ((iOver=rcThis.y+rcThis.Hgt-rClip.y-rClip.Hgt)>0)
|
|
{
|
|
if (iSplitCount) { AddRect(rcThis); pTarget = &Get(GetCount()-1); }
|
|
pTarget->y += pTarget->Hgt - iOver; pTarget->Hgt = iOver; rcThis.Hgt -= iOver;
|
|
++iSplitCount;
|
|
}
|
|
// clipped by left side
|
|
if ((iOver=rClip.x-rcThis.x)>0)
|
|
{
|
|
if (iSplitCount) { AddRect(rcThis); pTarget = &Get(GetCount()-1); }
|
|
pTarget->Wdt = iOver; rcThis.Wdt -= iOver; rcThis.x = rClip.x;
|
|
++iSplitCount;
|
|
}
|
|
// clipped by top side
|
|
if ((iOver=rClip.y-rcThis.y)>0)
|
|
{
|
|
if (iSplitCount) { AddRect(rcThis); pTarget = &Get(GetCount()-1); } else ++iSplitCount;
|
|
pTarget->Hgt = iOver; /* rcThis.Hgt -= iOver; rcThis.y = rClip.y; not needed, since rcThis is no longer used */
|
|
}
|
|
// nothing split? This means this rectnagle is completely contained
|
|
if (!iSplitCount)
|
|
{
|
|
// make it vanish
|
|
RemoveIndexedRect(i); --i;
|
|
}
|
|
}
|
|
// concat rectangles if possible
|
|
bool fDone = false;
|
|
while (!fDone)
|
|
{
|
|
fDone=true;
|
|
for (int32_t i = 0, cnt=GetCount(); i < cnt && fDone; ++i)
|
|
{
|
|
C4Rect &rc1 = Get(i);
|
|
for (int32_t j = i+1; j < cnt; ++j)
|
|
{
|
|
C4Rect &rc2 = Get(j);
|
|
if (rc1.y == rc2.y && rc1.Hgt == rc2.Hgt)
|
|
{
|
|
if (rc1.x + rc1.Wdt == rc2.x)
|
|
{
|
|
rc1.Wdt += rc2.Wdt; RemoveIndexedRect(j); fDone=false; break;
|
|
}
|
|
else if (rc2.x + rc2.Wdt == rc1.x)
|
|
{
|
|
rc2.Wdt += rc1.Wdt; RemoveIndexedRect(i); fDone=false; break;
|
|
}
|
|
}
|
|
else if (rc1.x == rc2.x && rc1.Wdt == rc2.Wdt)
|
|
{
|
|
if (rc1.y + rc1.Hgt == rc2.y)
|
|
{
|
|
rc1.Hgt += rc2.Hgt; RemoveIndexedRect(j); fDone=false; break;
|
|
}
|
|
else if (rc2.y + rc2.Hgt == rc1.y)
|
|
{
|
|
rc2.Hgt += rc1.Hgt; RemoveIndexedRect(i); fDone=false; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|