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 }
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; }
bool SimFlightHitsLiquid(C4Real fcx, C4Real fcy, C4Real xdir, C4Real ydir) { // Start in water? int temp; if (DensityLiquid(GBackDensity(fixtoi(fcx), fixtoi(fcy)))) if (!SimFlight(fcx, fcy, xdir, ydir, 0, C4M_Liquid - 1, temp=10)) return false; // Hits liquid? if (!SimFlight(fcx, fcy, xdir, ydir, C4M_Liquid, 100, temp=-1)) return false; // liquid & deep enough? return GBackLiquid(fixtoi(fcx), fixtoi(fcy)) && GBackLiquid(fixtoi(fcx), fixtoi(fcy) + 9); }
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 SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t iDensityMin, int32_t iDensityMax, int32_t &iIter) { bool hitOnTime = true; bool fBreak = false; int32_t ctcox,ctcoy,cx,cy,i; cx = fixtoi(x); cy = fixtoi(y); i = iIter; do { if (!--i) {hitOnTime = false; break;} // If the object isn't moving and there is no gravity either, abort if (xdir == 0 && ydir == 0 && GravAccel == 0) return false; // If the object is above the landscape flying upwards in no/negative gravity, abort if (ydir <= 0 && GravAccel <= 0 && cy < 0) return false; // Set target position by momentum x+=xdir; y+=ydir; // Movement to target ctcox=fixtoi(x); ctcoy=fixtoi(y); // Bounds if (!Inside<int32_t>(ctcox,0,GBackWdt) || (ctcoy>=GBackHgt)) return false; // Move to target do { // Set next step target cx+=Sign(ctcox-cx); cy+=Sign(ctcoy-cy); // Contact check if (Inside(GBackDensity(cx,cy), iDensityMin, iDensityMax)) { fBreak = true; break; } } while ((cx!=ctcox) || (cy!=ctcoy)); // Adjust GravAccel once per frame ydir+=GravAccel; } while (!fBreak); // write position back x = itofix(cx); y = itofix(cy); // how many steps did it take to get here? iIter -= i; return hitOnTime; }
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; int32_t motion_x = 0; 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 && ax >= 0 && ax < GBackWdt) { 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 && ax >= 0 && ax < GBackWdt) { // 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; } } } // both attachments: apply motion done by SolidMasks if (motion_x) cx += BoundBy<int32_t>(motion_x, -1, 1); #endif return fAttached; }
void C4PXS::Execute() { #ifdef DEBUGREC_PXS if (Config.General.DebugRec) { C4RCExecPXS rc; rc.x=x; rc.y=y; rc.iMat=Mat; rc.pos = 0; AddDbgRec(RCT_ExecPXS, &rc, sizeof(rc)); } #endif int32_t inmat; // Safety if (!MatValid(Mat)) { Deactivate(); return; } // Out of bounds if ((x<0) || (x>=::Landscape.GetWidth()) || (y<-10) || (y>=::Landscape.GetHeight())) { Deactivate(); return; } // Material conversion int32_t iX = fixtoi(x), iY = fixtoi(y); inmat=GBackMat(iX,iY); C4MaterialReaction *pReact = ::MaterialMap.GetReactionUnsafe(Mat, inmat); if (pReact && (*pReact->pFunc)(pReact, iX,iY, iX,iY, xdir,ydir, Mat,inmat, meePXSPos, NULL)) { Deactivate(); return; } // Gravity ydir+=GravAccel; if (GBackDensity(iX, iY + 1) < ::MaterialMap.Map[Mat].Density) { // Air speed: Wind plus some random int32_t iWind = Weather.GetWind(iX, iY); C4Real txdir = itofix(iWind, 15) + C4REAL256(Random(1200) - 600); C4Real tydir = C4REAL256(Random(1200) - 600); // Air friction, based on WindDrift. MaxSpeed is ignored. int32_t iWindDrift = std::max(::MaterialMap.Map[Mat].WindDrift - 20, 0); xdir += ((txdir - xdir) * iWindDrift) * WindDrift_Factor; ydir += ((tydir - ydir) * iWindDrift) * WindDrift_Factor; } C4Real ctcox = x + xdir; C4Real ctcoy = y + ydir; int32_t iToX = fixtoi(ctcox), iToY = fixtoi(ctcoy); // In bounds? if (Inside<int32_t>(iToX, 0, ::Landscape.GetWidth()-1) && Inside<int32_t>(iToY, 0, ::Landscape.GetHeight()-1)) // Check path if (::Landscape._PathFree(iX, iY, iToX, iToY)) { x=ctcox; y=ctcoy; return; } // Test path to target position int32_t iX0 = iX, iY0 = iY; bool fStopMovement = false; do { // Step int32_t inX = iX + Sign(iToX - iX), inY = iY + Sign(iToY - iY); // Contact? inmat = GBackMat(inX, inY); C4MaterialReaction *pReact = ::MaterialMap.GetReactionUnsafe(Mat, inmat); if (pReact) { if ((*pReact->pFunc)(pReact, iX,iY, inX,inY, xdir,ydir, Mat,inmat, meePXSMove, &fStopMovement)) { // destructive contact Deactivate(); return; } else { // no destructive contact, but speed or position changed: Stop moving for now if (fStopMovement) { // But keep fractional positions to allow proper movement on moving ground if (iX != iX0) x = itofix(iX); if (iY != iY0) y = itofix(iY); return; } // there was a reaction func, but it didn't do anything - continue movement } } iX = inX; iY = inY; } while (iX != iToX || iY != iToY); // No contact? Free movement x=ctcox; y=ctcoy; #ifdef DEBUGREC_PXS if (Config.General.DebugRec) { C4RCExecPXS rc; rc.x=x; rc.y=y; rc.iMat=Mat; rc.pos = 1; AddDbgRec(RCT_ExecPXS, &rc, sizeof(rc)); } #endif return; }
bool C4Particle::Exec(C4Object *obj, float timeDelta, C4ParticleDef *sourceDef) { // die of old age? :< lifetime -= timeDelta; // check only if we had a maximum lifetime to begin with (for permanent particles) if (startingLifetime > 0.f) { if (lifetime <= 0.f) return false; } // movement float currentForceX = properties.forceX.GetValue(this); float currentForceY = properties.forceY.GetValue(this); currentSpeedX += currentForceX; currentSpeedY += currentForceY; if (currentSpeedX != 0.f || currentSpeedY != 0.f) { float currentDampingX = properties.speedDampingX.GetValue(this); float currentDampingY = properties.speedDampingY.GetValue(this); float size = properties.size.GetValue(this); currentSpeedX *= currentDampingX; currentSpeedY *= currentDampingY; // move & collision check // note: accessing Landscape.GetDensity here is not protected by locks // it is assumed that the particle system is cleaned up before, f.e., the landscape memory is freed bool collided = false; if (properties.hasCollisionVertex) { float collisionPoint = properties.collisionVertex.GetValue(this); float size_x = (currentSpeedX > 0.f ? size : -size) * 0.5f * collisionPoint; float size_y = (currentSpeedY > 0.f ? size : -size) * 0.5f * collisionPoint; float density = static_cast<float>(GBackDensity(positionX + size_x + timeDelta * currentSpeedX, positionY + size_y + timeDelta * currentSpeedY)); if (density + 0.5f >= properties.collisionDensity.GetValue(this)) // Small offset against floating point insanities. { // exec collision func if (properties.collisionCallback != 0 && !(properties.*properties.collisionCallback)(this)) return false; collided = true; } } if (!collided) { positionX += timeDelta * currentSpeedX; positionY += timeDelta * currentSpeedY; } drawingData.SetPosition(positionX, positionY, size, properties.rotation.GetValue(this), properties.stretch.GetValue(this)); } else if(!properties.size.IsConstant() || !properties.rotation.IsConstant() || !properties.stretch.IsConstant()) { drawingData.SetPosition(positionX, positionY, properties.size.GetValue(this), properties.rotation.GetValue(this), properties.stretch.GetValue(this)); } // adjust color if (!properties.hasConstantColor) { drawingData.SetColor(properties.colorR.GetValue(this), properties.colorG.GetValue(this), properties.colorB.GetValue(this), properties.colorAlpha.GetValue(this)); } int currentPhase = (int)(properties.phase.GetValue(this) + 0.5f); if (currentPhase != drawingData.phase) drawingData.SetPhase(currentPhase, sourceDef); return true; }