bool ObjectComPunch(C4Object *cObj, C4Object *pTarget, int32_t punch) { if (!cObj || !pTarget) return false; if (!punch) return true; bool fBlowStopped = !!pTarget->Call(PSF_QueryCatchBlow,&C4AulParSet(cObj)); if (fBlowStopped && punch>1) punch=punch/2; // half damage for caught blow, so shield+armor help in fistfight and vs monsters pTarget->DoEnergy(-punch, false, C4FxCall_EngGetPunched, cObj->Controller); int32_t tdir=+1; if (cObj->Action.Dir==DIR_Left) tdir=-1; pTarget->Action.ComDir=COMD_Stop; // No tumbles when blow was caught if (fBlowStopped) return false; // Hard punch if (punch>=10) if (ObjectActionTumble(pTarget,pTarget->Action.Dir,C4REAL100(150)*tdir,itofix(-2))) { pTarget->Call(PSF_CatchBlow,&C4AulParSet(punch, cObj)); return true; } // Regular punch if (ObjectActionGetPunched(pTarget,C4REAL100(250)*tdir,Fix0)) { pTarget->Call(PSF_CatchBlow,&C4AulParSet(punch, cObj)); return true; } return false; }
bool C4MaterialMap::mrfScript(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged) { // do generic checks for user-defined reactions if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false; // check script func if (!pReaction->pScriptFunc) return false; // OK - let's call it! // 0 1 2 3 4 5 6 7 8 int32_t iXDir1, iYDir1, iXDir2, iYDir2; C4AulParSet pars(iX, iY, iLSPosX, iLSPosY, iXDir1 = fixtoi(fXDir, 100), iYDir1 = fixtoi(fYDir, 100), iPxsMat, iLsMat, int(evEvent)); if (!!pReaction->pScriptFunc->Exec(NULL, &pars, false)) { // PXS shall be killed! return true; } // PXS shall exist further: write back parameters iPxsMat = pars[6].getInt(); int32_t iX2 = pars[0].getInt(), iY2 = pars[1].getInt(); iXDir2 = pars[4].getInt(); iYDir2 = pars[5].getInt(); if (iX!=iX2 || iY!=iY2 || iXDir1!=iXDir2 || iYDir1!=iYDir2) { // changes to pos/speed detected if (pfPosChanged) *pfPosChanged = true; iX=iX2; iY=iY2; fXDir = C4REAL100(iXDir2); fYDir = C4REAL100(iYDir2); } // OK; done return false; }
bool ObjectComJump(C4Object *cObj) // by ObjectComUp, ExecCMDFMoveTo, FnJump { // Only if walking if (cObj->GetProcedure()!=DFA_WALK) return false; // Calculate direction & forces C4Real TXDir=Fix0; C4PropList *pActionWalk = cObj->GetAction(); C4Real iPhysicalWalk = C4REAL100(pActionWalk->GetPropertyInt(P_Speed)) * itofix(cObj->GetCon(), FullCon); C4Real iPhysicalJump = C4REAL100(cObj->GetPropertyInt(P_JumpSpeed)) * itofix(cObj->GetCon(), FullCon); if (cObj->Action.ComDir==COMD_Left || cObj->Action.ComDir==COMD_UpLeft) TXDir=-iPhysicalWalk; else if (cObj->Action.ComDir==COMD_Right || cObj->Action.ComDir==COMD_UpRight) TXDir=+iPhysicalWalk; C4Real x = cObj->fix_x, y = cObj->fix_y; // find bottom-most vertex, correct starting position for simulation int32_t iBtmVtx = cObj->Shape.GetBottomVertex(); if (iBtmVtx != -1) { x += cObj->Shape.GetVertexX(iBtmVtx); y += cObj->Shape.GetVertexY(iBtmVtx); } // Try dive if (cObj->Shape.ContactDensity > C4M_Liquid) if (SimFlightHitsLiquid(x,y,TXDir,-iPhysicalJump)) if (ObjectActionDive(cObj,TXDir,-iPhysicalJump)) return true; // Regular jump return ObjectActionJump(cObj,TXDir,-iPhysicalJump,true); }
void Splash(int32_t tx, int32_t ty, int32_t amt, C4Object *pByObj) { // Splash only if there is free space above if (GBackSemiSolid(tx, ty - 15)) return; // get back mat int32_t iMat = GBackMat(tx, ty); // check liquid if (MatValid(iMat)) if (DensityLiquid(::MaterialMap.Map[iMat].Density) && ::MaterialMap.Map[iMat].Instable) { int32_t sy = ty; while (GBackLiquid(tx, sy) && sy > ty - 20 && sy >= 0) sy--; // Splash bubbles and liquid for (int32_t cnt=0; cnt<amt; cnt++) { int32_t bubble_x = tx+Random(16)-8; int32_t bubble_y = ty+Random(16)-6; BubbleOut(bubble_x,bubble_y); if (GBackLiquid(tx,ty) && !GBackSemiSolid(tx, sy)) { C4Real xdir = C4REAL100(Random(151)-75); C4Real ydir = C4REAL100(-Random(200)); ::PXS.Create(::Landscape.ExtractMaterial(tx,ty,false), itofix(tx),itofix(sy), xdir, ydir); } } } // Splash sound if (amt>=20) StartSoundEffect("Splash2",false,100,pByObj); else if (amt>1) StartSoundEffect("Splash1",false,100,pByObj); }
bool mrfInsertCheck(int32_t &iX, int32_t &iY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, bool *pfPosChanged) { // always manipulating pos/speed here if (pfPosChanged) *pfPosChanged = true; // Move up by up to 3px to account for moving SolidMasks, other material insertions, etc. int32_t mdens = std::min(::MaterialMap.Map[iPxsMat].Density, C4M_Solid); int32_t max_upwards = 3; bool was_pushed_upwards = false; while (max_upwards-- && (::Landscape.GetDensity(iX, iY) >= mdens)) { --iY; was_pushed_upwards = true; } // Rough contact? May splash if (fYDir > itofix(1)) if (::MaterialMap.Map[iPxsMat].SplashRate && !Random(::MaterialMap.Map[iPxsMat].SplashRate)) { fYDir = -fYDir/8; fXDir = fXDir/8 + C4REAL100(Random(200) - 100); if (fYDir) return false; } // Contact: Stop fYDir = -GravAccel; // Incendiary mats smoke on contact even before doing their slide if (::MaterialMap.Map[iPxsMat].Incendiary) if (!Random(25)) { Smoke(iX, iY, 4 + Random(3)); } // Move by mat path/slide int32_t iSlideX = iX, iSlideY = iY; if (::Landscape.FindMatSlide(iSlideX,iSlideY,Sign(GravAccel),mdens,::MaterialMap.Map[iPxsMat].MaxSlide)) { // Sliding on equal material: Move directly to optimize insertion of rain onto lakes // Also move directly when shifted upwards to ensure movement on permamently moving SolidMask if (iPxsMat == iLsMat || was_pushed_upwards) { iX = iSlideX; iY = iSlideY; fXDir = 0; return false; } // Otherwise, just move using xdir/ydir for nice visuals when rain is moving over landscape // Accelerate into the direction fXDir = (fXDir * 10 + Sign(iSlideX - iX)) / 11 + C4REAL10(Random(5)-2); // Slide target in range? Move there directly. if (Abs(iX - iSlideX) <= Abs(fixtoi(fXDir))) { iX = iSlideX; iY = iSlideY; if (fYDir <= 0) fXDir = 0; } // Continue existance return false; } // insertion OK return true; }
void C4Sky::Execute() { // surface exists? if (!Surface) return; // advance pos x+=xdir; y+=ydir; // clip by bounds if (x>=itofix(Width)) x-=itofix(Width); if (y>=itofix(Height)) y-=itofix(Height); // update speed if (ParallaxMode == C4SkyPM_Wind) xdir=C4REAL100(::Weather.Wind); }
bool ObjectComDrop(C4Object *cObj, C4Object *pThing) { // No object specified, first from contents if (!pThing) pThing = cObj->Contents.GetObject(); // Nothing to throw if (!pThing) return false; // Force and direction // When dropping diagonally, drop from edge of shape // When doing a diagonal forward drop during flight, exit a bit closer to the Clonk to allow planned tumbling // Except when hangling, so you can mine effectively form the ceiling, and when swimming because you cannot tumble then C4Real pthrow=C4REAL100(cObj->GetPropertyInt(P_ThrowSpeed)); int32_t tdir=0; int right=0; bool isHanglingOrSwimming = false; int32_t iProc = -1; C4PropList* pActionDef = cObj->GetAction(); if (pActionDef) { iProc = pActionDef->GetPropertyP(P_Procedure); if (iProc == DFA_HANGLE || iProc == DFA_SWIM) isHanglingOrSwimming = true; } int32_t iOutposReduction = 1; // don't exit object too far forward during jump if (iProc != DFA_SCALE) // never diagonal during scaling (can have com into wall during scaling!) { if (ComDirLike(cObj->Action.ComDir, COMD_Left)) { tdir=-1; right = 0; if (cObj->xdir < C4REAL10(15) && !isHanglingOrSwimming) --iOutposReduction; } if (ComDirLike(cObj->Action.ComDir, COMD_Right)) { tdir=+1; right = 1; if (cObj->xdir > C4REAL10(-15) && !isHanglingOrSwimming) --iOutposReduction; } } // Exit object pThing->Exit(cObj->GetX() + (cObj->Shape.x + cObj->Shape.Wdt * right) * !!tdir * iOutposReduction, cObj->GetY()+cObj->Shape.y+cObj->Shape.Hgt-(pThing->Shape.y+pThing->Shape.Hgt),0,pthrow*tdir,Fix0,Fix0); // Update OCF cObj->SetOCF(); // Ungrab ObjectComUnGrab(cObj); // Done return true; }
bool ObjectActionThrow(C4Object *cObj, C4Object *pThing) { // No object specified, first from contents if (!pThing) pThing = cObj->Contents.GetObject(); // Nothing to throw if (!pThing) return false; // Force and direction C4Real pthrow=C4REAL100(cObj->GetPropertyInt(P_ThrowSpeed)); int32_t iDir=1; if (cObj->Action.Dir==DIR_Left) iDir=-1; // Set action if (!cObj->SetActionByName("Throw")) return false; // Exit object pThing->Exit(cObj->GetX(), cObj->GetY()+cObj->Shape.y-1, Random(360), pthrow*iDir+cObj->xdir,-pthrow+cObj->ydir,pthrow*iDir); // Success return true; }
*/ /* Object motion, collision, friction */ #include <C4Include.h> #include <C4Object.h> #include <C4Effect.h> #include <C4Physics.h> #include <C4SolidMask.h> #include <C4Landscape.h> #include <C4Game.h> /* Some physical constants */ const C4Real FRedirect=C4REAL100(50); const C4Real FFriction=C4REAL100(30); const C4Real FixFullCircle=itofix(360),FixHalfCircle=FixFullCircle/2; const C4Real FloatFriction=C4REAL100(2); const C4Real RotateAccel=C4REAL100(20); const C4Real HitSpeed1=C4REAL100(150); // Hit Event const C4Real HitSpeed2=itofix(2); // Cross Check Hit const C4Real HitSpeed3=itofix(6); // Scale disable, kneel const C4Real HitSpeed4=itofix(8); // Flat const C4Real DefaultGravAccel=C4REAL100(20); /* Some helper functions */ void RedirectForce(C4Real &from, C4Real &to, int32_t tdir) { C4Real fred;