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(Game.Material.Map[iMat].Density) && Game.Material.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++) { BubbleOut(tx + Random(16) - 8, ty + Random(16) - 6); if (GBackLiquid(tx, ty) && !GBackSemiSolid(tx, sy)) Game.PXS.Create(Game.Landscape.ExtractMaterial(tx, ty), itofix(tx), itofix(sy), FIXED100(Random(151) - 75), FIXED100(-Random(200))); } } // Splash sound if (amt >= 20) StartSoundEffect("Splash2", false, 100, pByObj); else if (amt > 1) StartSoundEffect("Splash1", false, 100, pByObj); }
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 C4MassMover::Init(int32_t tx, int32_t ty) { // Out of bounds check if (!Inside<int32_t>(tx,0,::Landscape.GetWidth()-1) || !Inside<int32_t>(ty,0,::Landscape.GetHeight()-1)) return false; // Check mat Mat=GBackMat(tx,ty); x=tx; y=ty; ::MassMover.Count++; return (Mat!=MNone); }
void C4EditCursor::UpdateStatusBar() { int32_t X=this->X, Y=this->Y; StdStrBuf str; switch (Mode) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case C4CNS_ModePlay: if (::MouseControl.GetCaption()) str.CopyUntil(::MouseControl.GetCaption(),'|'); break; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case C4CNS_ModeEdit: str.Format("%i/%i (%s)",X,Y,Target ? (Target->GetName()) : LoadResStr("IDS_CNS_NOTHING") ); break; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case C4CNS_ModeDraw: str.Format("%i/%i (%s)",X,Y,MatValid(GBackMat(X,Y)) ? ::MaterialMap.Map[GBackMat(X,Y)].Name : LoadResStr("IDS_CNS_NOTHING") ); break; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } Console.DisplayInfoText(C4ConsoleGUI::CONSOLE_Cursor, str); }
bool C4MassMover::Corrosion(int32_t dx, int32_t dy) { // check reaction map of massmover-mat to target mat int32_t tmat=GBackMat(x+dx,y+dy); C4MaterialReaction *pReact = ::MaterialMap.GetReactionUnsafe(Mat, tmat); if (pReact) { C4Real xdir=Fix0, ydir=Fix0; if ((*pReact->pFunc)(pReact, x,y, x+dx,y+dy, xdir,ydir, Mat,tmat, meeMassMove, NULL)) return true; } 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; }
bool C4MassMover::Execute() { int32_t tx,ty; // Lost target material if (GBackMat(x,y)!=Mat) { Cease(); return false; } // Check for transfer target space C4Material *pMat = ::MaterialMap.Map+Mat; tx=x; ty=y; if (!::Landscape.FindMatPath(tx,ty,+1,pMat->Density,pMat->MaxSlide)) { // Contact material reaction check: corrosion/evaporation/inflammation/etc. if (Corrosion(+0,+1) || Corrosion(-1,+0) || Corrosion(+1,+0)) { // material has been used up ::Landscape.ExtractMaterial(x,y,false); return true; } // No space, die Cease(); return false; } // do check at this pos for conversion check /* if (Corrosion(0,0)) { // material has been used up by conversion ::Landscape.ExtractMaterial(x,y); return true; }*/ // Transfer mass int32_t mat = ::Landscape.ExtractMaterial(x,y,false); if (Random(10)) ::Landscape.InsertDeadMaterial(mat, tx, ty); else ::Landscape.InsertMaterial(mat, &tx, &ty, 0, 1); // modifies tx/ty to actual insertion position // Create new mover at target ::MassMover.Create(tx,ty,!Random(3)); return true; }
void C4EditCursor::ApplyToolPicker() { int32_t iMaterial; BYTE byIndex; switch (::Landscape.Mode) { case C4LSC_Static: { bool material_set = false; // Material-texture from map if ((byIndex=::Landscape.GetMapIndex(X/::Landscape.MapZoom,Y/::Landscape.MapZoom))) { const C4TexMapEntry *pTex = ::TextureMap.GetEntry(byIndex & (IFT-1)); if (pTex && pTex->GetMaterialName() && *pTex->GetMaterialName()) { Console.ToolsDlg.SelectMaterial(pTex->GetMaterialName()); Console.ToolsDlg.SelectTexture(pTex->GetTextureName()); Console.ToolsDlg.SetIFT(!!(byIndex & ~(IFT-1))); material_set = true; } } // default to sky, because invalid materials are always rendered as sky if (!material_set) Console.ToolsDlg.SelectMaterial(C4TLS_MatSky); break; } case C4LSC_Exact: // Material only from landscape if (MatValid(iMaterial=GBackMat(X,Y))) { Console.ToolsDlg.SelectMaterial(::MaterialMap.Map[iMaterial].Name); Console.ToolsDlg.SetIFT(!!GBackIFT(X,Y)); } else Console.ToolsDlg.SelectMaterial(C4TLS_MatSky); break; } Hold=false; }
bool C4MaterialMap::mrfConvert(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) { if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false; switch (evEvent) { case meePXSMove: // PXS movement // for hardcoded stuff: only InMatConvert is Snow in Water, which does not have any collision proc if (!pReaction->fUserDefined) break; // user-defined conversions may also convert upon hitting materials case meePXSPos: // PXS check before movement { // Check depth int32_t iDepth = pReaction->fUserDefined ? pReaction->iDepth : ::MaterialMap.Map[iPxsMat].InMatConvertDepth; if (!iDepth || GBackMat(iX, iY - iDepth) == iLsMat) { // Convert iPxsMat = pReaction->fUserDefined ? pReaction->iConvertMat : ::MaterialMap.Map[iPxsMat].InMatConvertTo; if (!MatValid(iPxsMat)) // Convert failure (target mat not be loaded, or target may be C4TLS_MatSky): Kill Pix return true; // stop movement after conversion fXDir = fYDir = 0; if (pfPosChanged) *pfPosChanged = true; } } break; case meeMassMove: // MassMover-movement // Conversion-transfer to PXS ::PXS.Create(iPxsMat,itofix(iX),itofix(iY)); return true; } // not handled return false; }
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; }
int32_t FnFxFireStart(C4AulContext *ctx, C4Object *pObj, int32_t iNumber, int32_t iTemp, int32_t iCausedBy, bool fBlasted, C4Object *pIncineratingObject) { // safety if (!pObj) return -1; // temp readd if (iTemp) { pObj->SetOnFire(true); return 1; } // fail if already on fire if (pObj->GetOnFire()) return -1; // get associated effect C4Effect *pEffect; if (!(pEffect = pObj->pEffects)) return -1; if (!(pEffect = pEffect->Get(iNumber, true))) return -1; // structures must eject contents now, because DoCon is not guaranteed to be // executed! // In extinguishing material BOOL fFireCaused = TRUE; int32_t iMat; if (MatValid(iMat = GBackMat(pObj->x, pObj->y))) if (Game.Material.Map[iMat].Extinguisher) { // blasts should changedef in water, too! if (fBlasted) if (pObj->Def->BurnTurnTo != C4ID_None) pObj->ChangeDef(pObj->Def->BurnTurnTo); // no fire caused fFireCaused = FALSE; } // BurnTurnTo if (fFireCaused) if (pObj->Def->BurnTurnTo != C4ID_None) pObj->ChangeDef(pObj->Def->BurnTurnTo); // eject contents C4Object *cobj; if (!pObj->Def->IncompleteActivity && !pObj->Def->NoBurnDecay) while (cobj = pObj->Contents.GetObject()) { cobj->Controller = iCausedBy; // update controller, so incinerating a hut // full of flints attributes the damage to // the incinerator if (pObj->Contained) cobj->Enter(pObj->Contained); else cobj->Exit(cobj->x, cobj->y); } // Detach attached objects cobj = 0; if (!pObj->Def->IncompleteActivity && !pObj->Def->NoBurnDecay) while (cobj = Game.FindObject(0, 0, 0, 0, 0, OCF_All, 0, pObj, 0, 0, ANY_OWNER, cobj)) if ((cobj->Action.Act > ActIdle) && (cobj->Def->ActMap[cobj->Action.Act].Procedure == DFA_ATTACH)) cobj->SetAction(ActIdle); // fire caused? if (!fFireCaused) { // if object was blasted but not incinerated (i.e., inside extinguisher) // do a script callback if (fBlasted) pObj->Call(PSF_IncinerationEx, &C4AulParSet(C4VInt(iCausedBy))); return -1; } // determine fire appearance int32_t iFireMode; if (!(iFireMode = pObj->Call(PSF_FireMode).getInt())) { // set default fire modes DWORD dwCat = pObj->Category; if (dwCat & (C4D_Living | C4D_StaticBack)) // Tiere, Bäume iFireMode = C4Fx_FireMode_LivingVeg; else if (dwCat & (C4D_Structure | C4D_Vehicle)) // Gebäude und Fahrzeuge // sind unten meist kantig iFireMode = C4Fx_FireMode_StructVeh; else iFireMode = C4Fx_FireMode_Object; } else if (!Inside<int32_t>(iFireMode, 1, C4Fx_FireMode_Last)) { DebugLogF("Warning: FireMode %d of object %s (%s) is invalid!", iFireMode, pObj->GetName(), pObj->Def->GetName()); iFireMode = C4Fx_FireMode_Object; } // store causes in effect vars FxFireVarMode(pEffect).SetInt(iFireMode); FxFireVarCausedBy(pEffect) .SetInt(iCausedBy); // used in C4Object::GetFireCause and timer! FxFireVarBlasted(pEffect).SetBool(fBlasted); FxFireVarIncineratingObj(pEffect).SetObject(pIncineratingObject); // Set values pObj->SetOnFire(true); pObj->FirePhase = Random(MaxFirePhase); if (pObj->Shape.Wdt * pObj->Shape.Hgt > 500) StartSoundEffect("Inflame", false, 100, pObj); if (pObj->Def->Mass >= 100) StartSoundEffect("Fire", true, 100, pObj); // Engine script call pObj->Call(PSF_Incineration, &C4AulParSet(C4VInt(iCausedBy))); // Done, success return C4Fx_OK; }