void C4Landscape::UpdatePixMaps() // Copied from C4Landscape.cpp { int32_t i; for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Mat[i] = PixCol2Mat(i); for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Dens[i] = MatDensity(Pix2Mat[i]); for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Place[i] = MatValid(Pix2Mat[i]) ? ::MaterialMap.Map[Pix2Mat[i]].Placement : 0; for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Light[i] = MatValid(Pix2Mat[i]) && (::MaterialMap.Map[Pix2Mat[i]].Light>0); Pix2Place[0] = 0; // clear bridge mat conversion buffers for (int32_t i = 0; i < C4M_MaxTexIndex; ++i) { delete [] BridgeMatConversion[i]; BridgeMatConversion[i] = NULL; } }
bool C4MaterialMap::CrossMapMaterials(const char* szEarthMaterial) // Called after load { // Check material number if (::MaterialMap.Num>C4MaxMaterial) { LogFatal(LoadResStr("IDS_PRC_TOOMANYMATS")); return false; } // build reaction function map delete [] ppReactionMap; typedef C4MaterialReaction * C4MaterialReactionPtr; ppReactionMap = new C4MaterialReactionPtr[(Num+1)*(Num+1)]; for (int32_t iMatPXS=-1; iMatPXS<Num; iMatPXS++) { C4Material *pMatPXS = (iMatPXS+1) ? Map+iMatPXS : NULL; for (int32_t iMatLS=-1; iMatLS<Num; iMatLS++) { C4MaterialReaction *pReaction = NULL; C4Material *pMatLS = ( iMatLS+1) ? Map+ iMatLS : NULL; // natural stuff: material conversion here? if (pMatPXS && pMatPXS->sInMatConvert.getLength() && SEqualNoCase(pMatPXS->sInMatConvert.getData(), pMatLS ? pMatLS->Name : C4TLS_MatSky)) pReaction = &DefReactConvert; // non-sky reactions else if (pMatPXS && pMatLS) { // incindiary vs extinguisher if ((pMatPXS->Incendiary && pMatLS->Extinguisher) || (pMatPXS->Extinguisher && pMatLS->Incendiary)) pReaction = &DefReactPoof; // incindiary vs inflammable else if ((pMatPXS->Incendiary && pMatLS->Inflammable) || (pMatPXS->Inflammable && pMatLS->Incendiary)) pReaction = &DefReactIncinerate; // corrosive vs corrode else if (pMatPXS->Corrosive && pMatLS->Corrode) pReaction = &DefReactCorrode; // liquid hitting liquid or solid: Material insertion else if (DensityLiquid(MatDensity(iMatPXS)) && DensitySemiSolid(MatDensity(iMatLS))) pReaction = &DefReactInsert; // solid hitting solid: Material insertion else if (DensitySolid(MatDensity(iMatPXS)) && DensitySolid(MatDensity(iMatLS))) pReaction = &DefReactInsert; } // assign the function; or NULL for no reaction SetMatReaction(iMatPXS, iMatLS, pReaction); } } // reset max shape size max_shape_width=max_shape_height=0; // material-specific initialization int32_t cnt; for (cnt=0; cnt<Num; cnt++) { C4Material *pMat = Map+cnt; const char *szTextureOverlay = NULL; // newgfx: init pattern if (Map[cnt].sTextureOverlay.getLength()) if (::TextureMap.GetTexture(Map[cnt].sTextureOverlay.getLength())) { szTextureOverlay = Map[cnt].sTextureOverlay.getData(); // backwards compatibility: if a pattern was specified although the no-pattern flag was set, overwrite that flag if (Map[cnt].OverlayType & C4MatOv_None) { DebugLogF("Error in overlay of material %s: Flag C4MatOv_None ignored because a custom overlay (%s) was specified!", Map[cnt].Name, szTextureOverlay); Map[cnt].OverlayType &= ~C4MatOv_None; } } // default to first texture in texture map int iTexMapIx; if (!szTextureOverlay && (iTexMapIx = ::TextureMap.GetIndex(Map[cnt].Name, NULL, false))) szTextureOverlay = TextureMap.GetEntry(iTexMapIx)->GetTextureName(); // default to smooth if (!szTextureOverlay) szTextureOverlay = "none"; // search/create entry in texmap Map[cnt].DefaultMatTex = ::TextureMap.GetIndex(Map[cnt].Name, szTextureOverlay, true, FormatString("DefaultMatTex of mat %s", Map[cnt].Name).getData()); // init PXS facet C4Surface * sfcTexture; C4Texture * Texture; if (Map[cnt].sPXSGfx.getLength()) if ((Texture=::TextureMap.GetTexture(Map[cnt].sPXSGfx.getData()))) if ((sfcTexture=Texture->Surface32)) Map[cnt].PXSFace.Set(sfcTexture, Map[cnt].PXSGfxRt.x, Map[cnt].PXSGfxRt.y, Map[cnt].PXSGfxRt.Wdt, Map[cnt].PXSGfxRt.Hgt); // evaluate reactions for that material for (unsigned int iRCnt = 0; iRCnt < pMat->CustomReactionList.size(); ++iRCnt) { C4MaterialReaction *pReact = &(pMat->CustomReactionList[iRCnt]); if (pReact->sConvertMat.getLength()) pReact->iConvertMat = Get(pReact->sConvertMat.getData()); else pReact->iConvertMat = -1; // evaluate target spec int32_t tmat; if (MatValid(tmat=Get(pReact->TargetSpec.getData()))) { // single material target if (pReact->fInverseSpec) for (int32_t cnt2=-1; cnt2<Num; cnt2++) { if (cnt2!=tmat) SetMatReaction(cnt, cnt2, pReact); else SetMatReaction(cnt, tmat, pReact); } } else if (SEqualNoCase(pReact->TargetSpec.getData(), "All")) { // add to all materials, including sky if (!pReact->fInverseSpec) for (int32_t cnt2=-1; cnt2<Num; cnt2++) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Solid")) { // add to all solid materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (DensitySolid(Map[cnt2].Density) != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "SemiSolid")) { // add to all semisolid materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (DensitySemiSolid(Map[cnt2].Density) != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Background")) { // add to all BG materials, including sky if (!pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Density != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Sky")) { // add to sky if (!pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); else for (int32_t cnt2=0; cnt2<Num; cnt2++) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Incendiary") || SEqualNoCase(pReact->TargetSpec.getData(), "Incindiary")) { // add to all incendiary materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Incendiary == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Extinguisher")) { // add to all incendiary materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Extinguisher == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Inflammable")) { // add to all incendiary materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Inflammable == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Corrosive")) { // add to all incendiary materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Corrosive == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } else if (SEqualNoCase(pReact->TargetSpec.getData(), "Corrode")) { // add to all incendiary materials if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact); for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Corrode == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact); } } } // second loop (DefaultMatTex is needed by GetIndexMatTex) for (cnt=0; cnt<Num; cnt++) { if (Map[cnt].sBlastShiftTo.getLength()) Map[cnt].BlastShiftTo=::TextureMap.GetIndexMatTex(Map[cnt].sBlastShiftTo.getData(), NULL, true, FormatString("BlastShiftTo of mat %s", Map[cnt].Name).getData()); if (Map[cnt].sInMatConvertTo.getLength()) Map[cnt].InMatConvertTo=Get(Map[cnt].sInMatConvertTo.getData()); if (Map[cnt].sBelowTempConvertTo.getLength()) Map[cnt].BelowTempConvertTo=::TextureMap.GetIndexMatTex(Map[cnt].sBelowTempConvertTo.getData(), NULL, true, FormatString("BelowTempConvertTo of mat %s", Map[cnt].Name).getData()); if (Map[cnt].sAboveTempConvertTo.getLength()) Map[cnt].AboveTempConvertTo=::TextureMap.GetIndexMatTex(Map[cnt].sAboveTempConvertTo.getData(), NULL, true, FormatString("AboveTempConvertTo of mat %s", Map[cnt].Name).getData()); } // Get hardcoded system material indices const C4TexMapEntry* earth_entry = ::TextureMap.GetEntry(::TextureMap.GetIndexMatTex(szEarthMaterial)); if(!earth_entry) { LogFatal(FormatString("Earth material \"%s\" not found!", szEarthMaterial).getData()); return false; } MVehic = Get("Vehicle"); MCVehic = Mat2PixColDefault(MVehic); MHalfVehic = Get("HalfVehicle"); MCHalfVehic = Mat2PixColDefault(MHalfVehic); MTunnel = Get("Tunnel"); MWater = Get("Water"); MEarth = Get(earth_entry->GetMaterialName()); if ((MVehic==MNone) || (MTunnel==MNone)) { LogFatal(LoadResStr("IDS_PRC_NOSYSMATS")); return false; } return true; }
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; }