/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 1998-2000, Matthes Bender * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* Core component of a scenario file */ #include "C4Include.h" #include "landscape/C4Scenario.h" #include "lib/C4InputValidation.h" #include "lib/C4Random.h" #include "c4group/C4Group.h" #include "c4group/C4Components.h" #include "lib/StdColors.h" //==================================== C4SVal ============================================== C4SVal::C4SVal(int32_t std, int32_t rnd, int32_t min, int32_t max) : Std(std), Rnd(rnd), Min(min), Max(max) { } void C4SVal::Set(int32_t std, int32_t rnd, int32_t min, int32_t max) { Std=std; Rnd=rnd; Min=min; Max=max; } int32_t C4SVal::Evaluate() { return Clamp(Std+Random(2*Rnd+1)-Rnd,Min,Max); } void C4SVal::Default() { Set(); } void C4SVal::CompileFunc(StdCompiler *pComp) { pComp->Value(mkDefaultAdapt(Std, 0)); if (!pComp->Separator()) return; pComp->Value(mkDefaultAdapt(Rnd, 0)); if (!pComp->Separator()) return; pComp->Value(mkDefaultAdapt(Min, 0)); if (!pComp->Separator()) return; pComp->Value(mkDefaultAdapt(Max, 100)); } //================================ C4Scenario ========================================== C4Scenario::C4Scenario() { Default(); } void C4Scenario::Default() { int32_t cnt; Head.Default(); Definitions.Default(); Game.Default(); for (cnt=0; cnt(mkParAdapt(*this, fLoadSection), Buf, C4CFN_ScenarioCore)) { return false; } } else { if (!CompileFromBuf_LogWarn(mkParAdapt(*this, fLoadSection), Buf, C4CFN_ScenarioCore)) { return false; } } return true; } bool C4Scenario::Save(C4Group &hGroup, bool fSaveSection) { StdStrBuf Buf; try { Buf.Take(DecompileToBuf(mkParAdapt(*this, fSaveSection))); } catch (StdCompiler::Exception *) { return false; } if (!hGroup.Add(C4CFN_ScenarioCore,Buf,false,true)) { return false; } return true; } void C4Scenario::CompileFunc(StdCompiler *pComp, bool fSection) { pComp->Value(mkNamingAdapt(mkParAdapt(Head, fSection), "Head")); if (!fSection) pComp->Value(mkNamingAdapt(Definitions, "Definitions")); pComp->Value(mkNamingAdapt(mkParAdapt(Game, fSection), "Game")); for (int32_t i = 0; i < C4S_MaxPlayer; i++) pComp->Value(mkNamingAdapt(PlrStart[i], FormatString("Player%d", i+1).getData())); pComp->Value(mkNamingAdapt(Landscape, "Landscape")); pComp->Value(mkNamingAdapt(Animals, "Animals")); pComp->Value(mkNamingAdapt(Weather, "Weather")); pComp->Value(mkNamingAdapt(Environment, "Environment")); } int32_t C4Scenario::GetMinPlayer() { // MinPlayer is specified. if (Head.MinPlayer != 0) return Head.MinPlayer; // Otherwise/unknown: need at least one. return 1; } void C4SDefinitions::Default() { LocalOnly=AllowUserChange=false; ZeroMem(Definition,sizeof (Definition)); SkipDefs.Default(); } const int32_t C4S_MaxPlayerDefault = 12; void C4SHead::Default() { Origin.Clear(); Icon=18; *Title = *Loader = *Font = *Engine = *MissionAccess = '\0'; Secret = false; C4XVer[0] = C4XVer[1] = 0; Difficulty = RandomSeed = 0; SaveGame = Replay = NoInitialize = false; Film = 0; NetworkGame = NetworkRuntimeJoin = false; MaxPlayer=MaxPlayerLeague=C4S_MaxPlayerDefault; MinPlayer=0; // auto-determine by mode SCopy("Default Title",Title,C4MaxTitle); } void C4SHead::CompileFunc(StdCompiler *pComp, bool fSection) { if (!fSection) { pComp->Value(mkNamingAdapt(Icon, "Icon", 18)); pComp->Value(mkNamingAdapt(mkStringAdaptMA(Title), "Title", "Default Title")); pComp->Value(mkNamingAdapt(mkStringAdaptMA(Loader), "Loader", "")); pComp->Value(mkNamingAdapt(mkStringAdaptMA(Font), "Font", "")); pComp->Value(mkNamingAdapt(mkArrayAdaptDM(C4XVer,0), "Version" )); pComp->Value(mkNamingAdapt(Difficulty, "Difficulty", 0)); pComp->Value(mkNamingAdapt(MaxPlayer, "MaxPlayer", C4S_MaxPlayerDefault)); pComp->Value(mkNamingAdapt(MaxPlayerLeague, "MaxPlayerLeague", MaxPlayer)); pComp->Value(mkNamingAdapt(MinPlayer, "MinPlayer", 0)); pComp->Value(mkNamingAdapt(SaveGame, "SaveGame", false)); pComp->Value(mkNamingAdapt(Replay, "Replay", false)); pComp->Value(mkNamingAdapt(Film, "Film", 0)); } pComp->Value(mkNamingAdapt(NoInitialize, "NoInitialize", false)); pComp->Value(mkNamingAdapt(RandomSeed, "RandomSeed", 0)); if (!fSection) { pComp->Value(mkNamingAdapt(mkStringAdaptMA(Engine), "Engine", "")); pComp->Value(mkNamingAdapt(mkStringAdaptMA(MissionAccess), "MissionAccess", "")); pComp->Value(mkNamingAdapt(Secret, "Secret", false)); pComp->Value(mkNamingAdapt(NetworkGame, "NetworkGame", false)); pComp->Value(mkNamingAdapt(NetworkRuntimeJoin, "NetworkRuntimeJoin", false)); pComp->Value(mkNamingAdapt(mkStrValAdapt(mkParAdapt(Origin, StdCompiler::RCT_All), C4InVal::VAL_SubPathFilename), "Origin", StdCopyStrBuf())); // windows needs backslashes in Origin; other systems use forward slashes if (pComp->isCompiler()) Origin.ReplaceChar(AltDirectorySeparator, DirectorySeparator); } } void C4SGame::Default() { Goals.Clear(); Rules.Clear(); FoWEnabled = true; } void C4SGame::CompileFunc(StdCompiler *pComp, bool fSection) { if (!fSection) { pComp->Value(mkNamingAdapt(Realism.ValueOverloads, "ValueOverloads", C4IDList())); } pComp->Value(mkNamingAdapt(mkRuntimeValueAdapt(Realism.LandscapePushPull), "LandscapePushPull", false)); pComp->Value(mkNamingAdapt(mkRuntimeValueAdapt(Realism.LandscapeInsertThrust), "LandscapeInsertThrust",true)); pComp->Value(mkNamingAdapt(mkParAdapt(Mode, StdCompiler::RCT_IdtfAllowEmpty), "Mode", StdCopyStrBuf())); pComp->Value(mkNamingAdapt(Goals, "Goals", C4IDList())); pComp->Value(mkNamingAdapt(Rules, "Rules", C4IDList())); pComp->Value(mkNamingAdapt(FoWEnabled, "FoWEnabled", true)); } void C4SPlrStart::Default() { Wealth.Set(0,0,0,250); Position[0]=Position[1]=-1; EnforcePosition=0; ReadyCrew.Default(); ReadyBase.Default(); ReadyVehic.Default(); ReadyMaterial.Default(); BuildKnowledge.Default(); BaseMaterial.Default(); BaseProduction.Default(); } bool C4SPlrStart::EquipmentEqual(C4SPlrStart &rhs) { return *this == rhs; } bool C4SPlrStart::operator==(const C4SPlrStart& rhs) { return (Wealth == rhs.Wealth) && (ReadyCrew == rhs.ReadyCrew) && (ReadyBase == rhs.ReadyBase) && (ReadyVehic == rhs.ReadyVehic) && (ReadyMaterial == rhs.ReadyMaterial) && (BuildKnowledge == rhs.BuildKnowledge) && (BaseMaterial == rhs.BaseMaterial) && (BaseProduction == rhs.BaseProduction); } void C4SPlrStart::CompileFunc(StdCompiler *pComp) { C4IDList crewDefault; crewDefault.SetIDCount(C4ID::Clonk,1,true); pComp->Value(mkNamingAdapt(Wealth, "Wealth", C4SVal(0, 0, 0,250), true)); pComp->Value(mkNamingAdapt(mkArrayAdaptDM(Position,-1), "Position" )); pComp->Value(mkNamingAdapt(EnforcePosition, "EnforcePosition", 0)); pComp->Value(mkNamingAdapt(ReadyCrew, "Crew", crewDefault)); pComp->Value(mkNamingAdapt(ReadyBase, "Buildings", C4IDList())); pComp->Value(mkNamingAdapt(ReadyVehic, "Vehicles", C4IDList())); pComp->Value(mkNamingAdapt(ReadyMaterial, "Material", C4IDList())); pComp->Value(mkNamingAdapt(BuildKnowledge, "Knowledge", C4IDList())); pComp->Value(mkNamingAdapt(BaseMaterial, "BaseMaterial", C4IDList())); pComp->Value(mkNamingAdapt(BaseProduction, "BaseProduction", C4IDList())); } void C4SLandscape::Default() { BottomOpen=0; TopOpen=1; LeftOpen=0; RightOpen=0; AutoScanSideOpen=1; SkyDef[0]=0; for (int32_t cnt=0; cnt<6; cnt++) SkyDefFade[cnt]=0; VegLevel.Set(50,30,0,100); Vegetation.Default(); InEarthLevel.Set(50,0,0,100); InEarth.Default(); MapWdt.Set(100,0,64,250); MapHgt.Set(50,0,40,250); MapZoom.Set(8,0,5,15); Amplitude.Set(0,0); Phase.Set(50); Period.Set(15); Random.Set(0); LiquidLevel.Default(); MapPlayerExtend=0; Layers.Clear(); SCopy("Earth",Material,C4M_MaxName); SCopy("Water",Liquid,C4M_MaxName); ExactLandscape=0; Gravity.Set(100,0,10,200); NoScan=0; KeepMapCreator=0; SkyScrollMode=0; MaterialZoom=4; FlatChunkShapes=false; } void C4SLandscape::GetMapSize(int32_t &rWdt, int32_t &rHgt, int32_t iPlayerNum) { rWdt = MapWdt.Evaluate(); rHgt = MapHgt.Evaluate(); iPlayerNum = std::max( iPlayerNum, 1 ); if (MapPlayerExtend) rWdt = std::min(rWdt * std::min(iPlayerNum, C4S_MaxMapPlayerExtend), MapWdt.Max); } void C4SLandscape::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(ExactLandscape, "ExactLandscape", false)); pComp->Value(mkNamingAdapt(Vegetation, "Vegetation", C4IDList())); pComp->Value(mkNamingAdapt(VegLevel, "VegetationLevel", C4SVal(50,30,0,100), true)); pComp->Value(mkNamingAdapt(InEarth, "InEarth", C4IDList())); pComp->Value(mkNamingAdapt(InEarthLevel, "InEarthLevel", C4SVal(50,0,0,100), true)); pComp->Value(mkNamingAdapt(mkStringAdaptMA(SkyDef), "Sky", "")); pComp->Value(mkNamingAdapt(mkArrayAdaptDM(SkyDefFade,0),"SkyFade" )); pComp->Value(mkNamingAdapt(BottomOpen, "BottomOpen", 0)); pComp->Value(mkNamingAdapt(TopOpen, "TopOpen", 1)); pComp->Value(mkNamingAdapt(LeftOpen, "LeftOpen", 0)); pComp->Value(mkNamingAdapt(RightOpen, "RightOpen", 0)); pComp->Value(mkNamingAdapt(AutoScanSideOpen, "AutoScanSideOpen", true)); pComp->Value(mkNamingAdapt(MapWdt, "MapWidth", C4SVal(100,0,64,250), true)); pComp->Value(mkNamingAdapt(MapHgt, "MapHeight", C4SVal(50,0,40,250), true)); pComp->Value(mkNamingAdapt(MapZoom, "MapZoom", C4SVal(8,0,5,15), true)); pComp->Value(mkNamingAdapt(Amplitude, "Amplitude", C4SVal(0))); pComp->Value(mkNamingAdapt(Phase, "Phase", C4SVal(50))); pComp->Value(mkNamingAdapt(Period, "Period", C4SVal(15))); pComp->Value(mkNamingAdapt(Random, "Random", C4SVal(0))); pComp->Value(mkNamingAdapt(mkStringAdaptMA(Material),"Material", "Earth")); pComp->Value(mkNamingAdapt(mkStringAdaptMA(Liquid), "Liquid", "Water")); pComp->Value(mkNamingAdapt(LiquidLevel, "LiquidLevel", C4SVal())); pComp->Value(mkNamingAdapt(MapPlayerExtend, "MapPlayerExtend", 0)); pComp->Value(mkNamingAdapt(Layers, "Layers", C4NameList())); pComp->Value(mkNamingAdapt(Gravity, "Gravity", C4SVal(100,0,10,200), true)); pComp->Value(mkNamingAdapt(NoScan, "NoScan", false)); pComp->Value(mkNamingAdapt(KeepMapCreator, "KeepMapCreator", false)); pComp->Value(mkNamingAdapt(SkyScrollMode, "SkyScrollMode", 0)); pComp->Value(mkNamingAdapt(MaterialZoom, "MaterialZoom", 4)); pComp->Value(mkNamingAdapt(FlatChunkShapes, "FlatChunkShapes", false)); } void C4SWeather::Default() { Climate.Set(50,10); StartSeason.Set(50,50); YearSpeed.Set(50); Wind.Set(0,70,-100,+100); NoGamma=1; } void C4SWeather::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(Climate, "Climate", C4SVal(50,10), true)); pComp->Value(mkNamingAdapt(StartSeason, "StartSeason", C4SVal(50,50), true)); pComp->Value(mkNamingAdapt(YearSpeed, "YearSpeed", C4SVal(50))); pComp->Value(mkNamingAdapt(Wind, "Wind", C4SVal(0,70,-100,+100), true)); pComp->Value(mkNamingAdapt(NoGamma, "NoGamma", true)); } void C4SAnimals::Default() { FreeLife.Clear(); EarthNest.Clear(); } void C4SAnimals::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(FreeLife, "Animal", C4IDList())); pComp->Value(mkNamingAdapt(EarthNest, "Nest", C4IDList())); } void C4SEnvironment::Default() { Objects.Clear(); } void C4SEnvironment::CompileFunc(StdCompiler *pComp) { pComp->Value(mkNamingAdapt(Objects, "Objects", C4IDList())); } void C4SRealism::Default() { LandscapePushPull=0; LandscapeInsertThrust=0; ValueOverloads.Default(); } void C4Scenario::Clear() { } void C4Scenario::SetExactLandscape() { if (Landscape.ExactLandscape) return; // Set landscape Landscape.ExactLandscape = 1; } bool C4SDefinitions::GetModules(StdStrBuf *psOutModules) const { // Local only if (LocalOnly) { psOutModules->Copy(""); return true; } // Scan for any valid entries bool fSpecified = false; int32_t cnt=0; for (; cntCopy(""); for (cnt=0; cntgetLength()) psOutModules->AppendChar(';'); psOutModules->Append(Definition[cnt]); } // Done return true; } void C4SDefinitions::SetModules(const char *szList, const char *szRelativeToPath, const char *szRelativeToPath2) { int32_t cnt; // Empty list: local only if (!SModuleCount(szList)) { LocalOnly=true; for (cnt=0; cntValue(mkNamingAdapt(LocalOnly, "LocalOnly", false)); pComp->Value(mkNamingAdapt(AllowUserChange, "AllowUserChange", false)); pComp->Value(mkNamingAdapt(mkStringAdaptMA(Definition[0]), "Definition1", "Objects.ocd")); for (int32_t i = 1; i < C4S_MaxDefinitions; i++) pComp->Value(mkNamingAdapt(mkStringAdaptMA(Definition[i]), FormatString("Definition%i", i+1).getData(), "")); pComp->Value(mkNamingAdapt(SkipDefs, "SkipDefs", C4IDList())); } bool C4SGame::IsMelee() { // Check for game modes known to be melees // Also allow it in parkours by default because that works fine if (Mode == "Melee" || Mode == "Parkour") return true; // Game mode not present or unknown? Check for old MELE goal which was still used by some scenarios. if (Goals.GetIDCount(C4ID::Melee, 1)) return true; // Nothing looks like melee here return false; }