openclonk/src/landscape/C4Scenario.cpp

485 lines
16 KiB
C++

/*
* 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<int32_t>(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<C4S_MaxPlayer; cnt++) PlrStart[cnt].Default();
Landscape.Default();
Animals.Default();
Weather.Default();
Game.Realism.Default();
Environment.Default();
}
bool C4Scenario::Load(C4Group &hGroup, bool fLoadSection, bool suppress_errors)
{
StdStrBuf Buf;
if (!hGroup.LoadEntryString(C4CFN_ScenarioCore,&Buf)) return false;
if (!fLoadSection) Default();
if (suppress_errors)
{
if (!CompileFromBuf_Log<StdCompilerINIRead>(mkParAdapt(*this, fLoadSection), Buf, C4CFN_ScenarioCore))
{
return false;
}
}
else
{
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(*this, fLoadSection), Buf, C4CFN_ScenarioCore))
{
return false;
}
}
return true;
}
bool C4Scenario::Save(C4Group &hGroup, bool fSaveSection)
{
StdStrBuf Buf;
try
{
Buf.Take(DecompileToBuf<StdCompilerINIWrite>(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<int32_t>( 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 (; cnt<C4S_MaxDefinitions; cnt++)
if (Definition[cnt][0])
fSpecified = true;
// No valid entries
if (!fSpecified) return false;
// Compose entry list
psOutModules->Copy("");
for (cnt=0; cnt<C4S_MaxDefinitions; cnt++)
if (Definition[cnt][0])
{
if (psOutModules->getLength()) 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; cnt<C4S_MaxDefinitions; cnt++) Definition[cnt][0]=0;
return;
}
// Set list
LocalOnly=false;
for (cnt=0; cnt<C4S_MaxDefinitions; cnt++)
{
SGetModule(szList,cnt,Definition[cnt],_MAX_PATH);
// Make relative path
if (szRelativeToPath && *szRelativeToPath)
{
if (GetRelativePathS(Definition[cnt],szRelativeToPath) != Definition[cnt])
{
SCopy(GetRelativePathS(Definition[cnt],szRelativeToPath),Definition[cnt]);
continue;
}
}
if (szRelativeToPath2 && *szRelativeToPath2)
{
if (GetRelativePathS(Definition[cnt],szRelativeToPath2) != Definition[cnt])
{
SCopy(GetRelativePathS(Definition[cnt],szRelativeToPath2),Definition[cnt]);
continue;
}
}
}
}
void C4SDefinitions::CompileFunc(StdCompiler *pComp)
{
pComp->Value(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;
}