int32_t C4TextureMap::GetIndex(const char *szMaterial, const char *szTexture, BOOL fAddIfNotExist, const char *szErrorIfFailed) { BYTE byIndex; // Find existing for (byIndex = 1; byIndex < C4M_MaxTexIndex; byIndex++) if (!Entry[byIndex].isNull()) if (SEqualNoCase(Entry[byIndex].GetMaterialName(), szMaterial)) if (!szTexture || SEqualNoCase(Entry[byIndex].GetTextureName(), szTexture)) return byIndex; // Add new entry if (fAddIfNotExist) for (byIndex=1; byIndex<C4M_MaxTexIndex; byIndex++) if (Entry[byIndex].isNull()) { if (AddEntry(byIndex, szMaterial, szTexture)) { fEntriesAdded=true; return byIndex; } #ifdef C4ENGINE if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: Init failed.", szMaterial, szTexture, szErrorIfFailed); #endif return 0; } // Else, fail #ifdef C4ENGINE if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: %s.", szMaterial, szTexture, szErrorIfFailed, fAddIfNotExist ? "Map is full!" : "Entry not found."); #endif return 0; }
bool C4TexMapEntry::Init() { // Find material iMaterialIndex = ::MaterialMap.Get(Material.getData()); if (!MatValid(iMaterialIndex)) { DebugLogF("Error initializing material %s-%s: Invalid material!", Material.getData(), Texture.getData()); return false; } pMaterial = &::MaterialMap.Map[iMaterialIndex]; // Find texture StdStrBuf FirstTexture; FirstTexture.CopyUntil(Texture.getData(), '-'); C4Texture * sfcTexture = ::TextureMap.GetTexture(FirstTexture.getData()); if (!sfcTexture) { DebugLogF("Error initializing material %s-%s: Invalid texture!", Material.getData(), FirstTexture.getData()); Clear(); return false; } // Get overlay properties int32_t iOverlayType=pMaterial->OverlayType; int32_t iZoom=0; if (iOverlayType & C4MatOv_Exact) iZoom=1; if (iOverlayType & C4MatOv_HugeZoom) iZoom=4; // Create pattern MatPattern.Set(sfcTexture->Surface32, iZoom); return true; }
void StdCompilerWarnCallback(void *pData, const char *szPosition, const char *szError) { const char *szName = reinterpret_cast<const char *>(pData); if (!szPosition || !*szPosition) DebugLogF("WARNING: %s (in %s)", szError, szName); else DebugLogF("WARNING: %s (in %s, %s)", szError, szPosition, szName); }
void C4Def::LoadMeshMaterials(C4Group &hGroup, C4DefGraphicsPtrBackup *gfx_backup) { // Load all mesh materials from this folder C4DefAdditionalResourcesLoader loader(hGroup); hGroup.ResetSearch(); char MaterialFilename[_MAX_PATH + 1]; *MaterialFilename = 0; for (const auto &mat : mesh_materials) { ::MeshMaterialManager.Remove(mat, &gfx_backup->GetUpdater()); } mesh_materials.clear(); while (hGroup.FindNextEntry(C4CFN_DefMaterials, MaterialFilename, NULL, !!*MaterialFilename)) { StdStrBuf material; if (hGroup.LoadEntryString(MaterialFilename, &material)) { try { StdStrBuf buf; buf.Copy(hGroup.GetName()); buf.Append("/"); buf.Append(MaterialFilename); auto new_materials = ::MeshMaterialManager.Parse(material.getData(), buf.getData(), loader); mesh_materials.insert(new_materials.begin(), new_materials.end()); } catch (const StdMeshMaterialError& ex) { DebugLogF("Failed to read material script: %s", ex.what()); } } } }
int32_t C4TextureMap::GetIndexMatTex(const char *szMaterialTexture, const char *szDefaultTexture, BOOL fAddIfNotExist, const char *szErrorIfFailed) { // split material/texture pair StdStrBuf Material, Texture; Material.CopyUntil(szMaterialTexture, '-'); Texture.Copy(SSearch(szMaterialTexture, "-")); // texture not given or invalid? int32_t iMatTex = 0; if (Texture.getData()) if (iMatTex = GetIndex(Material.getData(), Texture.getData(), fAddIfNotExist)) return iMatTex; if (szDefaultTexture) if (iMatTex = GetIndex(Material.getData(), szDefaultTexture, fAddIfNotExist)) return iMatTex; #ifdef C4ENGINE // search material long iMaterial = Game.Material.Get(szMaterialTexture); if (!MatValid(iMaterial)) { if (szErrorIfFailed) DebugLogF("Error getting MatTex for %s: Invalid material", szErrorIfFailed); return 0; } // return default map entry return Game.Material.Map[iMaterial].DefaultMatTex; #else return 0; #endif }
bool C4DefGraphics::LoadMesh(C4Group &hGroup, const char* szFileName, StdMeshSkeletonLoader& loader) { char* buf = NULL; size_t size; try { if(!hGroup.LoadEntry(szFileName, &buf, &size, 1)) return false; if (SEqualNoCase(GetExtension(szFileName), "xml")) { Mesh = StdMeshLoader::LoadMeshXml(buf, size, ::MeshMaterialManager, loader, hGroup.GetName()); } else { Mesh = StdMeshLoader::LoadMeshBinary(buf, size, ::MeshMaterialManager, loader, hGroup.GetName()); } delete[] buf; Mesh->SetLabel(pDef->id.ToString()); // order submeshes Mesh->PostInit(); } catch (const std::runtime_error& ex) { DebugLogF("Failed to load mesh in definition %s: %s", hGroup.GetName(), ex.what()); delete[] buf; return false; } Type = TYPE_Mesh; return true; }
bool C4DefGraphics::LoadSkeleton(C4Group &hGroup, const char* szFileName, StdMeshSkeletonLoader& loader) { char* buf = NULL; size_t size; try { if (!hGroup.LoadEntry(szFileName, &buf, &size, 1)) return false; // delete skeleton from the map for reloading, or else if you delete or rename // a skeleton file in the folder the old skeleton will still exist in the map loader.RemoveSkeleton(hGroup.GetName(), szFileName); if (SEqualNoCase(GetExtension(szFileName), "xml")) { loader.LoadSkeletonXml(hGroup.GetName(), szFileName, buf, size); } else { loader.LoadSkeletonBinary(hGroup.GetName(), szFileName, buf, size); } delete[] buf; } catch (const std::runtime_error& ex) { DebugLogF("Failed to load skeleton in definition %s: %s", hGroup.GetName(), ex.what()); delete[] buf; return false; } return true; }
bool C4DefGraphics::LoadSkeleton(C4Group &hGroup, const char* szFileName, StdMeshSkeletonLoader& loader) { char* buf = NULL; size_t size; try { if (!hGroup.LoadEntry(szFileName, &buf, &size, 1)) return false; StdCopyStrBuf filename = StdCopyStrBuf(); StdMeshSkeletonLoader::MakeFullSkeletonPath(filename, hGroup.GetName(), szFileName); if (SEqualNoCase(GetExtension(szFileName), "xml")) { loader.LoadSkeletonXml(filename, buf, size); } else { loader.LoadSkeletonBinary(filename, buf, size); } delete[] buf; } catch (const std::runtime_error& ex) { DebugLogF("Failed to load skeleton in definition %s: %s", hGroup.GetName(), ex.what()); delete[] buf; return false; } return true; }
bool C4Def::LoadGraphics(C4Group &hGroup, StdMeshSkeletonLoader &loader) { // Try to load graphics // No fail on error - just have an object without graphics. Graphics.Load(hGroup, loader, !!ColorByOwner); if (Graphics.Type == C4DefGraphics::TYPE_Bitmap) { // Bitmap post-load settings if (Graphics.GetBitmap()) { // Set MainFace (unassigned bitmap: will be set by GetMainFace()) MainFace.Set(NULL, 0, 0, Shape.Wdt, Shape.Hgt); } // Adjust picture rect if ((PictureRect.Wdt == 0) || (PictureRect.Hgt == 0)) PictureRect.Set(0, 0, Shape.Wdt*Graphics.Bmp.Bitmap->Scale, Shape.Hgt*Graphics.Bmp.Bitmap->Scale); // validate TopFace if (TopFace.x<0 || TopFace.y<0 || TopFace.x + TopFace.Wdt>Graphics.Bmp.Bitmap->Wdt || TopFace.y + TopFace.Hgt>Graphics.Bmp.Bitmap->Hgt) { TopFace.Default(); // warn in debug mode DebugLogF("invalid TopFace in %s (%s)", GetName(), id.ToString()); } } else { TopFace.Default(); } return true; }
bool C4ParticleDef::Load(C4Group &group) { // store file Filename.Copy(group.GetFullName()); // load char *particle_source; if (group.LoadEntry(C4CFN_ParticleCore,&particle_source,NULL,1)) { if (!Compile(particle_source, Filename.getData())) { DebugLogF("invalid particle def at '%s'", group.GetFullName().getData()); delete [] particle_source; return false; } delete [] particle_source; // load graphics if (!Gfx.Load(group, C4CFN_DefGraphics, C4FCT_Full, C4FCT_Full, false, C4SF_MipMap)) { DebugLogF("particle %s has no valid graphics defined", Name.getData()); return false; } // set facet, if assigned - otherwise, assume full surface if (GfxFace.Wdt) Gfx.Set(Gfx.Surface, GfxFace.x, GfxFace.y, GfxFace.Wdt, GfxFace.Hgt); // set phase num int32_t Q; Gfx.GetPhaseNum(PhasesX, Q); Length = PhasesX * Q; if (!Length) { DebugLogF("invalid facet for particle '%s'", Name.getData()); return false; } // calc aspect Aspect=(float) Gfx.Hgt/Gfx.Wdt; // particle overloading C4ParticleDef *def_overload; if ((def_overload = Particles.definitions.GetDef(Name.getData(), this))) { if (Config.Graphics.VerboseObjectLoading >= 1) { char ostr[250]; sprintf(ostr,LoadResStr("IDS_PRC_DEFOVERLOAD"),def_overload->Name.getData(),"<particle>"); Log(ostr); } delete def_overload; } // success return true; } return false; }
void C4MaterialReaction::ResolveScriptFuncs(const char *szMatName) { // get script func for script-defined behaviour if (pFunc == &C4MaterialMap::mrfScript) { pScriptFunc = ::ScriptEngine.GetPropList()->GetFunc(this->ScriptFunc.getData()); if (!pScriptFunc) DebugLogF("Error getting function \"%s\" for Material reaction of \"%s\"", this->ScriptFunc.getData(), szMatName); } else pScriptFunc = NULL; }
bool C4Def::LoadSolidMask(C4Group &hGroup) { if (hGroup.FindEntry(C4CFN_SolidMask)) { pSolidMask = C4SolidMask::LoadMaskFromFile(hGroup, C4CFN_SolidMask); if (!pSolidMask) { DebugLogF(" Error loading SolidMask of %s (%s)", hGroup.GetFullName().getData(), id.ToString()); return false; } // check SolidMask size if (SolidMask.x<0 || SolidMask.y<0 || SolidMask.x + SolidMask.Wdt>pSolidMask->Wdt || SolidMask.y + SolidMask.Hgt>pSolidMask->Hgt) SolidMask.Default(); } else if (SolidMask.Wdt) { // Warning in case someone wants to define SolidMasks the old way (in the main graphics file) DebugLogF("WARNING: Definition %s (%s) defines SolidMask in DefCore but has no SolidMask file!", hGroup.GetFullName().getData(), id.ToString()); SolidMask.Default(); } return true; }
bool C4TexMapEntry::Init() { #ifdef C4ENGINE // Find material iMaterialIndex = Game.Material.Get(Material.getData()); if(!MatValid(iMaterialIndex)) { DebugLogF("Error initializing material %s-%s: Invalid material!", Material.getData(), Texture.getData()); return false; } pMaterial = &Game.Material.Map[iMaterialIndex]; // Special, hardcoded crap: change <liquid>-Smooth to <liquid>-Liquid const char *szTexture = Texture.getData(); if (DensityLiquid(pMaterial->Density)) if (SEqualNoCase(szTexture, "Smooth")) szTexture = "Liquid"; // Find texture C4Texture * sfcTexture = Game.TextureMap.GetTexture(szTexture); if(!sfcTexture) { DebugLogF("Error initializing material %s-%s: Invalid texture!", Material.getData(), Texture.getData()); Clear(); return false; } // Get overlay properties int32_t iOverlayType=pMaterial->OverlayType; bool fMono = !!(iOverlayType & C4MatOv_Monochrome); int32_t iZoom=0; if (iOverlayType & C4MatOv_Exact) iZoom=1; if (iOverlayType & C4MatOv_HugeZoom) iZoom=4; // Create pattern if (sfcTexture->Surface32) MatPattern.Set(sfcTexture->Surface32, iZoom, fMono); else MatPattern.Set(sfcTexture->Surface8, iZoom, fMono); MatPattern.SetColors(pMaterial->Color, pMaterial->Alpha); #endif return true; }
C4SoundInstance *C4SoundSystem::NewEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier) { // Sound not active if (!Config.Sound.RXSound) return NULL; // Get sound C4SoundEffect *csfx; if (!(csfx = GetEffect(szSndName))) { // Warn about missing or incorrectly spelled sound to allow finding mistakes earlier. DebugLogF("Warning: could not find sound matching '%s'", szSndName); return NULL; } // Play return csfx->New(fLoop, iVolume, pObj, iCustomFalloffDistance, iPitch, modifier); }
void CStdMultimediaTimerProc::SetDelay(uint32_t iDelay) { // Kill old timer (of any) if (idCriticalTimer) timeKillEvent(idCriticalTimer); // Set new delay uCriticalTimerDelay = iDelay; // Set critical timer idCriticalTimer=timeSetEvent( uCriticalTimerDelay,uCriticalTimerResolution, (LPTIMECALLBACK) Event.GetEvent(),0,TIME_PERIODIC | TIME_CALLBACK_EVENT_SET); if(idCriticalTimer == 0) DebugLogF("Creating Critical Timer failed: %d", GetLastError()); }
bool C4Def::LoadDefCore(C4Group &hGroup) { StdStrBuf Source; if (hGroup.LoadEntryString(C4CFN_DefCore,&Source)) { StdStrBuf Name = hGroup.GetFullName() + (const StdStrBuf &)FormatString("%cDefCore.txt", DirectorySeparator); if (!Compile(Source.getData(), Name.getData())) return false; Source.Clear(); // Check mass if (Mass < 0) { DebugLogF("WARNING: Def %s (%s) at %s has invalid mass!", GetName(), id.ToString(), hGroup.GetFullName().getData()); Mass = 0; } return true; } return false; }
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 C4DefGraphics::LoadBitmap(C4Group &hGroup, const char *szFilename, const char *szOverlay, const char *szNormal, bool fColorByOwner) { if (!szFilename) return false; Type = TYPE_Bitmap; // will be reset to TYPE_None in Clear() if loading fails Bmp.Bitmap = new C4Surface(); if (!Bmp.Bitmap->Load(hGroup, szFilename, false, true, C4SF_MipMap)) { Clear(); return false; } // Create owner color bitmaps if (fColorByOwner) { // Create additionmal bitmap Bmp.BitmapClr=new C4Surface(); // if overlay-surface is present, load from that if (szOverlay && Bmp.BitmapClr->Load(hGroup, szOverlay, false, false, C4SF_MipMap)) { // set as Clr-surface, also checking size if (!Bmp.BitmapClr->SetAsClrByOwnerOf(Bmp.Bitmap)) { DebugLogF(" Gfx loading error in %s: %s (%d x %d) doesn't match overlay %s (%d x %d) - invalid file or size mismatch", hGroup.GetFullName().getData(), szFilename, Bmp.Bitmap ? Bmp.Bitmap->Wdt : -1, Bmp.Bitmap ? Bmp.Bitmap->Hgt : -1, szOverlay, Bmp.BitmapClr->Wdt, Bmp.BitmapClr->Hgt); Clear(); return false; } } else { // otherwise, create by all blue shades if (!Bmp.BitmapClr->CreateColorByOwner(Bmp.Bitmap)) { Clear(); return false; } } fColorBitmapAutoCreated = true; } if (szNormal) { Bmp.BitmapNormal = new C4Surface(); if (Bmp.BitmapNormal->Load(hGroup, szNormal, false, true, C4SF_MipMap)) { // Normal map loaded. Sanity check and link. if(Bmp.BitmapNormal->Wdt != Bmp.Bitmap->Wdt || Bmp.BitmapNormal->Hgt != Bmp.Bitmap->Hgt) { DebugLogF(" Gfx loading error in %s: %s (%d x %d) doesn't match normal %s (%d x %d) - invalid file or size mismatch", hGroup.GetFullName().getData(), szFilename, Bmp.Bitmap ? Bmp.Bitmap->Wdt : -1, Bmp.Bitmap ? Bmp.Bitmap->Hgt : -1, szNormal, Bmp.BitmapNormal->Wdt, Bmp.BitmapNormal->Hgt); Clear(); return false; } Bmp.Bitmap->pNormalSfc = Bmp.BitmapNormal; if(Bmp.BitmapClr) Bmp.BitmapClr->pNormalSfc = Bmp.BitmapNormal; } else { // No normal map delete Bmp.BitmapNormal; Bmp.BitmapNormal = NULL; } } Type = TYPE_Bitmap; // success return true; }
void OnError(const char *msg) override { DebugLogF("ERROR: %s", msg); ++::ScriptEngine.errCnt; }
void OnWarning(const char *msg) override { DebugLogF("WARNING: %s", msg); ++::ScriptEngine.warnCnt; }
void C4GameObjects::FixObjectOrder() { // fixes the object order so it matches the global object order sorting // constraints C4ObjectLink *pLnk0 = First, *pLnkL = Last; while (pLnk0 != pLnkL) { C4ObjectLink *pLnk1stUnsorted = NULL, *pLnkLastUnsorted = NULL, *pLnkPrev = NULL, *pLnk; C4Object *pLastWarnObj = NULL; // forward fix uint32_t dwLastCategory = C4D_SortLimit; for (pLnk = pLnk0; pLnk != pLnkL->Next; pLnk = pLnk->Next) { C4Object *pObj = pLnk->Obj; if (pObj->Unsorted || !pObj->Status) continue; DWORD dwCategory = pObj->Category & C4D_SortLimit; // must have exactly one SortOrder-bit set if (!dwCategory) { DebugLogF("Objects.txt: Object #%d is missing sorting category!", (int)pObj->Number); ++pObj->Category; dwCategory = 1; } else { DWORD dwCat2 = dwCategory; int i = 0; while (~dwCat2 & 1) { dwCat2 = dwCat2 >> 1; ++i; } if (dwCat2 != 1) { DebugLogF("Objects.txt: Object #%d has invalid sorting category %x!", (int)pObj->Number, (unsigned int)dwCategory); dwCategory = (1 << i); pObj->Category = (pObj->Category & ~C4D_SortLimit) | dwCategory; } } // fix order if (dwCategory > dwLastCategory) { // SORT ERROR! (note that pLnkPrev can't be 0) if (pLnkPrev->Obj != pLastWarnObj) { DebugLogF("Objects.txt: Wrong object order of #%d-#%d! (down)", (int)pObj->Number, (int)pLnkPrev->Obj->Number); pLastWarnObj = pLnkPrev->Obj; } pLnk->Obj = pLnkPrev->Obj; pLnkPrev->Obj = pObj; pLnkLastUnsorted = pLnkPrev; } else dwLastCategory = dwCategory; pLnkPrev = pLnk; } if (!pLnkLastUnsorted) break; // done pLnkL = pLnkLastUnsorted; // backwards fix dwLastCategory = 0; for (pLnk = pLnkL; pLnk != pLnk0->Prev; pLnk = pLnk->Prev) { C4Object *pObj = pLnk->Obj; if (pObj->Unsorted || !pObj->Status) continue; DWORD dwCategory = pObj->Category & C4D_SortLimit; if (dwCategory < dwLastCategory) { // SORT ERROR! (note that pLnkPrev can't be 0) if (pLnkPrev->Obj != pLastWarnObj) { DebugLogF("Objects.txt: Wrong object order of #%d-#%d! (up)", (int)pObj->Number, (int)pLnkPrev->Obj->Number); pLastWarnObj = pLnkPrev->Obj; } pLnk->Obj = pLnkPrev->Obj; pLnkPrev->Obj = pObj; pLnk1stUnsorted = pLnkPrev; } else dwLastCategory = dwCategory; pLnkPrev = pLnk; } if (!pLnk1stUnsorted) break; // done pLnk0 = pLnk1stUnsorted; } // objects fixed! }
int C4GameObjects::Load(C4Group &hGroup, bool fKeepInactive) { Clear(!fKeepInactive); // Load data component StdStrBuf Source; if (!hGroup.LoadEntryString(C4CFN_ScenarioObjects, Source)) return 0; // Compile StdStrBuf Name = hGroup.GetFullName() + DirSep C4CFN_ScenarioObjects; if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(*this, false), Source, Name.getData())) return 0; // Process objects C4ObjectLink *cLnk = nullptr; C4Object *pObj = nullptr; bool fObjectNumberCollision = false; int32_t iMaxObjectNumber = 0; for (cLnk = Last; cLnk; cLnk = cLnk->Prev) { C4Object *pObj = cLnk->Obj; // check object number collision with inactive list if (fKeepInactive) { for (C4ObjectLink *clnk = InactiveObjects.First; clnk; clnk = clnk->Next) if (clnk->Obj->Number == pObj->Number) fObjectNumberCollision = true; } // keep track of numbers iMaxObjectNumber = Max<long>(iMaxObjectNumber, pObj->Number); // add to list of backobjects if (pObj->Category & C4D_Background) Game.BackObjects.Add(pObj, C4ObjectList::stMain, this); // add to list of foreobjects if (pObj->Category & C4D_Foreground) Game.ForeObjects.Add(pObj, C4ObjectList::stMain, this); // Unterminate end } // Denumerate pointers // if object numbers collideded, numbers will be adjusted afterwards // so fake inactive object list empty meanwhile C4ObjectLink *pInFirst = nullptr; if (fObjectNumberCollision) { pInFirst = InactiveObjects.First; InactiveObjects.First = NULL; } // denumerate pointers Denumerate(); // update object enumeration index now, because calls like UpdateTransferZone // might create objects Game.ObjectEnumerationIndex = Max(Game.ObjectEnumerationIndex, iMaxObjectNumber); // end faking and adjust object numbers if (fObjectNumberCollision) { InactiveObjects.First = pInFirst; // simply renumber all inactive objects for (cLnk = InactiveObjects.First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) pObj->Number = ++Game.ObjectEnumerationIndex; } // special checks: // -contained/contents-consistency // -StaticBack-objects zero speed for (cLnk = First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) { // staticback must not have speed if (pObj->Category & C4D_StaticBack) { pObj->xdir = pObj->ydir = 0; } // contained must be in contents list if (pObj->Contained) if (!pObj->Contained->Contents.GetLink(pObj)) { DebugLogF( "Error in Objects.txt: Container of #%d is #%d, but not found in " "contents list!", pObj->Number, pObj->Contained->Number); pObj->Contained->Contents.Add(pObj, C4ObjectList::stContents); } // all contents must have contained set; otherwise, remove them! C4Object *pObj2; for (C4ObjectLink *cLnkCont = pObj->Contents.First; cLnkCont; cLnkCont = cLnkCont->Next) { // check double links if (pObj->Contents.GetLink(cLnkCont->Obj) != cLnkCont) { DebugLogF("Error in Objects.txt: Double containment of #%d by #%d!", cLnkCont->Obj->Number, pObj->Number); // this remove-call will only remove the previous (dobuled) link, so // cLnkCont should be save pObj->Contents.Remove(cLnkCont->Obj); // contents checked already continue; } // check contents/contained-relation if ((pObj2 = cLnkCont->Obj)->Status) if (pObj2->Contained != pObj) { DebugLogF( "Error in Objects.txt: Object #%d not in container #%d as " "referenced!", pObj2->Number, pObj->Number); pObj2->Contained = pObj; } } } // sort out inactive objects C4ObjectLink *cLnkNext; for (cLnk = First; cLnk; cLnk = cLnkNext) { cLnkNext = cLnk->Next; if (cLnk->Obj->Status == C4OS_INACTIVE) { if (cLnk->Prev) cLnk->Prev->Next = cLnkNext; else First = cLnkNext; if (cLnkNext) cLnkNext->Prev = cLnk->Prev; else Last = cLnk->Prev; if (cLnk->Prev = InactiveObjects.Last) InactiveObjects.Last->Next = cLnk; else InactiveObjects.First = cLnk; InactiveObjects.Last = cLnk; cLnk->Next = NULL; Mass -= pObj->Mass; } } { C4DebugRecOff DBGRECOFF; // - script callbacks that would kill // DebugRec-sync for runtime start // update graphics UpdateGraphics(false); // Update faces UpdateFaces(false); // Update ocf SetOCF(); } // make sure list is sorted by category - after sorting out inactives, because // inactives aren't sorted into the main list FixObjectOrder(); // Sectors.Dump(); // misc updates for (cLnk = First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) { // add to plrview pObj->PlrFoWActualize(); // update flipdir (for old objects.txt with no flipdir defined) // assigns Action.DrawDir as well pObj->UpdateFlipDir(); } // Done return ObjectCount(); }
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; }