Added scenario achievements displayed as small symbols beside the scenario name in the startup selection screen.

They can be used e.g. to show that you have finished a scenario on different difficulty levels.
issue1247
Sven Eberhardt 2014-09-24 23:08:40 +02:00
parent 378bda5546
commit abab7be591
25 changed files with 714 additions and 255 deletions

View File

@ -457,6 +457,8 @@ set(OC_CLONK_SOURCES
src/player/C4PlayerList.h
src/player/C4RankSystem.cpp
src/player/C4RankSystem.h
src/player/C4ScenarioParameters.cpp
src/player/C4ScenarioParameters.h
src/script/C4AulDebug.cpp
src/script/C4AulDebug.h
thirdparty/timsort/sort.h

View File

@ -47,6 +47,12 @@
<col>Value of default option that is chosen if the user does not select anything.</col>
<col>0</col>
</row>
<row>
<col>Achievement</col>
<col>String</col>
<col>If set to an achievement ID, this parameter is hidden in the selection dialogue and only used to track player achievements for this scenario. See section below.</col>
<col></col>
</row>
</table>
</text>
<text>Each parameter of type Enumeration should be followed by subordinate section [Options], which contains several subordinate sections [Option]. Subordinate sections need to be indented further than their parent section.</text>
@ -79,6 +85,11 @@
</row>
</table>
</text>
<h>Achievements</h>
<text>Scenario achievements are special parameter definitions for which the values are stored per player and per scenario. These parameters cannot be set maually, but can only be gained in game using the <funclink>GainScenarioAchievement</funclink> function. Each achievement that has been gained is represented by a small image beside the scenario title in the scenario selection dialogue.</text>
<text>Achievements need to be defined as parameter definition with the Achievement value set in the [ParameterDef] section. The value corresponds to the icon file name formatted as Achv*.png, where * is replaced by the achievement name. The icon may either be placed in the scenario or - if multiple scenarios share the same icon - in the global Graphics.ocg file. Icons are square with the height determining the dimensions.</text>
<text>By using achievement values higher than one, multiple stages of an achievement may be defined e.g. for finishing a scenario on different difficulties. The achievement value denotes the 1-based index in the icon file with multiple icons arranged from left to right.</text>
<text>If several players are activated while the scenario selection dialogue is opened, the player with the highest achievement value is considered for each achievement.</text>
<h>Example</h>
<text>Example ParameterDef.txt file for a difficulty setting in a scenario that controls the amount of meteorites:</text>
<code>[ParameterDef]
@ -95,19 +106,40 @@ Default=15
[Option]
Name=$Hard$
Description=$DescHard$
Value=100</code>
Value=100
[ParameterDef]
ID=Done
Achievement=Done
[Options]
[Option]
Description=$AchvEasy$
Value=1
[Option]
Description=$AchvHard$
Value=3</code>
<text>The names and descriptions are localized strings which can be set for English in a StringTblUS.txt:</text>
<code>Difficulty=Difficulty
Easy=Easy
Hard=Hard
DescDifficulty=Conrols the amount of meteorites
DescEasy=Very few meteorites
DescHard=Lots of meteorites</code>
<text>Finally, the Script.c file of the scenario should contain code to evaluate the setting:</text>
DescHard=Lots of meteorites
AchvEasy=Finished on easy.
AchvHard=Finished on hard.</code>
<text>Finally, the Script.c file of the scenario should contain code to evaluate the setting and give an achievement accordingly:</text>
<code>func Initialize()
{
// Meteorite amount depending on difficulty setting
Meteor->SetChance(SCENPAR_Difficulty);
}
func OnGoalsFulfilled()
{
<funclink>GainScenarioAchievement</funclink>("Done", BoundBy(SCENPAR_Difficulty, 1, 3)); // 1 for easy, 3 for hard
return false;
}</code>
<author>Sven2</author><date>2014-09</date>
</doc>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>GainScenarioAchievement</title>
<category>Player</category>
<version>5.4 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>string</type>
<name>id</name>
<desc>ID of achievement to gain. Must be defined as a custom scenario parameter for the scenario.</desc>
</param>
<param>
<type>value</type>
<name>int</name>
<desc>Value to set for this achievement. Defaults to 1.</desc>
<optional />
</param>
<param>
<type>player</type>
<name>int</name>
<desc>Value to set for this achievement. If nil or NO_OWNER, the achievement is gained for all player currently in the game.</desc>
<optional />
</param>
<param>
<type>string</type>
<name>scenario_name</name>
<desc>Can be set to the filename of another scenario to gain an achievement for that scenario. Scenario paths contain all .oc* paths, e.g. "Missions.ocf\Raid.ocs". Defaults to the filename of the current scenario or its origin.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Gain a scenario achievement. Gained achievements are represented as small symbols beside the scenario name in the selection screen.</desc>
<remark>Make sure the gain the achievement before players are saved, i.e. before the game is evaluated. Otherwise, gained achievements are not stored.</remark>
<related><funclink>GainMissionAccess</funclink></related>
<related><emlink href="scenario/ParameterDefs.xml">Scenario parameter definitions</emlink></related>
</func>
<author>Sven2</author><date>2014-09</date>
</funcs>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -14,3 +14,16 @@ Default=1
Name=$Hard$
Description=$DescHard$
Value=99
[ParameterDef]
ID=Done
Achievement=Done
[Options]
[Option]
Description=$AchvEasy$
Value=1
[Option]
Description=$AchvHard$
Value=3

View File

@ -1,4 +1,26 @@
static num_to_collect;
func Initialize()
{
Log("Difficulty = %d", SCENPAR_Difficulty);
num_to_collect = SCENPAR_Difficulty;
for (var i=0; i<num_to_collect; ++i)
{
var ng = CreateObject(Nugget, Random(LandscapeWidth()-100) + 50, 10);
ng.Entrance = Scenario.GotNugget;
}
return true;
}
func GotNugget()
{
--num_to_collect;
if (num_to_collect == 0) GameCall("Finished");
}
func Finished()
{
Log("All nuggets collected!");
GainScenarioAchievement("Done", BoundBy(SCENPAR_Difficulty, 1, 3));
GameOver();
}

View File

@ -4,3 +4,5 @@ Hard=Schwer
DescDifficulty=Setzt SCENPAR_Difficulty auf einen Wert.
DescEasy=SCENPAR_Difficulty ist 1
DescHard=SCENPAR_Difficulty ist 99
AchvEasy=Auf einfach geschafft.
AchvHard=Auf schwer geschafft.

View File

@ -4,3 +4,5 @@ Hard=Hard
DescDifficulty=Sets SCENPAR_Difficulty to a value.
DescEasy=SCENPAR_Difficulty is 1
DescHard=SCENPAR_Difficulty is 99
AchvEasy=Finished on easy.
AchvHard=Finished on hard.

View File

@ -82,6 +82,7 @@
#define C4CFN_ScenarioObjects "Objects.txt"
#define C4CFN_ScenarioDesc "Desc%s.rtf"
#define C4CFN_DefMaterials "*.material"
#define C4CFN_Achievements "Achv*.png"
#define C4CFN_DefMesh "Graphics.mesh"
#define C4CFN_DefMeshXml C4CFN_DefMesh ".xml"
@ -169,7 +170,7 @@
// TODO: proper sorting of scaled def graphics (once we know what order we might load them in...)
#define C4FLS_Scenario "Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|Fonts.txt|Scenario.txt|Title*.txt|Info.txt|Desc*.rtf|Icon.png|Icon.bmp|Game.txt|StringTbl*.txt|ParameterDefs.txt|Teams.txt|Parameters.txt|Info.txt|Sect*.ocg|Music.ocg|*.mid|*.wav|Desc*.rtf|Title.png|Title.jpg|*.ocd|Material.ocg|MatMap.txt|Landscape.bmp|Landscape.png|" C4CFN_DiffLandscape "|Sky.bmp|Sky.png|Sky.jpeg|Sky.jpg|PXS.ocb|MassMover.ocb|CtrlRec.ocb|Strings.txt|Objects.txt|RoundResults.txt|Author.txt|Version.txt|Names.txt|*.ocd|Script.c|Script*.c|Map.c|Objects.c|System.ocg"
#define C4FLS_Scenario "Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|Fonts.txt|Scenario.txt|Title*.txt|Info.txt|Desc*.rtf|Icon.png|Icon.bmp|Achv*.png|Game.txt|StringTbl*.txt|ParameterDefs.txt|Teams.txt|Parameters.txt|Info.txt|Sect*.ocg|Music.ocg|*.mid|*.wav|Desc*.rtf|Title.png|Title.jpg|*.ocd|Material.ocg|MatMap.txt|Landscape.bmp|Landscape.png|" C4CFN_DiffLandscape "|Sky.bmp|Sky.png|Sky.jpeg|Sky.jpg|PXS.ocb|MassMover.ocb|CtrlRec.ocb|Strings.txt|Objects.txt|RoundResults.txt|Author.txt|Version.txt|Names.txt|*.ocd|Script.c|Script*.c|Map.c|Objects.c|System.ocg"
#define C4FLS_Section "Scenario.txt|Game.txt|Landscape.bmp|Landscape.png|Sky.bmp|Sky.png|Sky.jpeg|Sky.jpg|PXS.ocb|MassMover.ocb|CtrlRec.ocb|Strings.txt|Objects.txt|Objects.c"
#define C4FLS_SectionLandscape "Scenario.txt|Landscape.bmp|Landscape.png|PXS.ocb|MassMover.ocb"
#define C4FLS_SectionObjects "Strings.txt|Objects.txt|Objects.c"
@ -179,7 +180,7 @@
#define C4FLS_Folder "Folder.txt|Title*.txt|Info.txt|Desc*.rtf|Title.png|Title.jpg|Icon.png|Icon.bmp|Author.txt|Version.txt|*.ocs|Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|FolderMap.txt|FolderMap.png"
#define C4FLS_Material "TexMap.txt|*.bmp|*.png|*.ocm"
#define C4FLS_Graphics "Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|Font*.png"\
"|Control.png|Fire.png|Background.png|Flag.png|Crew.png|Wealth.png|Player.png|Rank.png|Captain.png|Cursor.png|SelectMark.png|MenuSymbol.png|Menu.png|Logo.png|Construction.png|Energy.png|Options.png|UpperBoard.png|Arrow.png|Exit.png|Hand.png|Gamepad.png|Build.png"\
"|Control.png|Fire.png|Background.png|Flag.png|Crew.png|Wealth.png|Player.png|Rank.png|Captain.png|Cursor.png|SelectMark.png|MenuSymbol.png|Menu.png|Logo.png|Construction.png|Energy.png|Options.png|UpperBoard.png|Arrow.png|Exit.png|Hand.png|Gamepad.png|Build.png|Achv*.png"\
"|GUIProgress.png|Endeavour.ttf|GUICaption.png|GUIButton.png|GUIButtonDown.png|GUIButtonHighlight.png|GUIButtonHighlightRound.png|GUIIcons.png|GUIIcons2.png|GUIScroll.png|GUIContext.png|GUISubmenu.png|GUICheckBox.png|GUIBigArrows.png"\
"|StartupMainMenuBG.*|StartupScenSelBG.*|StartupPlrSelBG.*|StartupPlrPropBG.*|StartupNetworkBG.*|StartupAboutBG.*|StartupBigButton.png|StartupBigButtonDown.png|StartupBookScroll.png|StartupContext.png|StartupScenSelIcons.png|StartupScenSelTitleOv.png|StartupDlgPaper.png|StartupOptionIcons.png|StartupTabClip.png|StartupNetGetRef.png|StartupLogo.png"
#define C4FLS_Objects "Names*.txt|Desc*.txt|*.ocd"

View File

@ -371,152 +371,6 @@ void C4GameResList::CompileFunc(StdCompiler *pComp)
}
// *** C4ScenarioParameters
void C4ScenarioParameterDef::Option::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("Option")) { pComp->NameEnd(); pComp->excNotFound("Option"); }
pComp->Value(mkNamingAdapt(mkParAdapt(Name, StdCompiler::RCT_All), "Name", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkParAdapt(Description, StdCompiler::RCT_All), "Description", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt( Value, "Value", 0));
pComp->NameEnd();
}
const C4ScenarioParameterDef::Option *C4ScenarioParameterDef::GetOptionByValue(int32_t val) const
{
// search option by value
for (auto i = Options.cbegin(); i != Options.cend(); ++i)
if (i->Value == val)
return &*i;
return NULL;
}
const C4ScenarioParameterDef::Option *C4ScenarioParameterDef::GetOptionByIndex(size_t idx) const
{
if (idx >= Options.size()) return NULL;
return &Options[idx];
}
void C4ScenarioParameterDef::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("ParameterDef")) { pComp->NameEnd(); pComp->excNotFound("ParameterDef"); }
pComp->Value(mkNamingAdapt(mkParAdapt(Name, StdCompiler::RCT_All), "Name", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkParAdapt(Description, StdCompiler::RCT_All), "Description", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkParAdapt(ID, StdCompiler::RCT_Idtf), "ID", StdCopyStrBuf()));
StdEnumEntry<ParameterType> ParTypeEntries[] =
{
{ "Enumeration", SPDT_Enum },
{ NULL, SPDT_Enum }
};
pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(Type, ParTypeEntries), "Type", SPDT_Enum));
pComp->Value(mkNamingAdapt(Default, "Default", 0));
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Options, StdCompiler::SEP_NONE), "Options"));
pComp->NameEnd();
}
void C4ScenarioParameterDefs::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkSTLContainerAdapt(Parameters, StdCompiler::SEP_NONE));
}
const C4ScenarioParameterDef *C4ScenarioParameterDefs::GetParameterDefByIndex(size_t idx) const
{
if (idx >= Parameters.size()) return NULL;
return &Parameters[idx];
}
bool C4ScenarioParameterDefs::Load(C4Group &hGroup, C4LangStringTable *pLang)
{
// Load buffer, localize and parse
StdStrBuf Buf;
if (!hGroup.LoadEntryString(C4CFN_ScenarioParameterDefs,&Buf)) return false;
if (pLang) pLang->ReplaceStrings(Buf);
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Buf, C4CFN_ScenarioCore))
{ return false; }
return true;
}
void C4ScenarioParameterDefs::RegisterScriptConstants(const C4ScenarioParameters &values)
{
// register constants for all parameters in script engine
for (auto i = Parameters.cbegin(); i != Parameters.cend(); ++i)
{
StdStrBuf constant_name;
constant_name.Format("SCENPAR_%s", i->GetID());
int32_t constant_value = values.GetValueByID(i->GetID(), i->GetDefault());
::ScriptEngine.RegisterGlobalConstant(constant_name.getData(), C4VInt(constant_value));
}
}
int32_t C4ScenarioParameters::GetValueByID(const char *id, int32_t default_value) const
{
// return map value if name is in map. Otherwise, return default value.
auto i = Parameters.find(StdStrBuf(id));
if (i != Parameters.end()) return i->second; else return default_value;
}
void C4ScenarioParameters::SetValue(const char *id, int32_t value)
{
// just update map
Parameters[StdStrBuf(id)] = value;
}
void C4ScenarioParameters::CompileFunc(StdCompiler *pComp)
{
// Unfortunately, StdCompiler cannot save std::map yet
if (pComp->isCompiler())
{
Parameters.clear();
if (pComp->hasNaming())
{
// load from INI
size_t name_count = pComp->NameCount();
for (size_t i=0; i<name_count; ++i)
{
int32_t v=0;
const char *name = pComp->GetNameByIndex(0); // always get name index 0, because names are removed after values have been extracted for them
StdCopyStrBuf sName(name);
if (!name) continue;
pComp->Value(mkNamingAdapt(v, sName.getData(), 0));
Parameters[sName] = v;
}
}
else
{
// load from binary
int32_t name_count=0;
pComp->Value(name_count);
for (int32_t i=0; i<name_count; ++i)
{
StdCopyStrBuf name; int32_t v;
pComp->Value(name);
pComp->Value(v);
Parameters[name] = v;
}
}
}
else
{
if (pComp->hasNaming())
{
// save to INI
for (auto i = Parameters.begin(); i != Parameters.end(); ++i)
pComp->Value(mkNamingAdapt(i->second, i->first.getData()));
}
else
{
// save to binary
int32_t name_count=Parameters.size();
pComp->Value(name_count);
for (auto i = Parameters.begin(); i != Parameters.end(); ++i)
{
pComp->Value(const_cast<StdCopyStrBuf &>(i->first));
pComp->Value(i->second);
}
}
}
}
// *** C4GameParameters

View File

@ -22,6 +22,7 @@
#include "C4PlayerInfo.h"
#include "C4LangStringTable.h"
#include "C4Teams.h"
#include "C4InfoCore.h"
class C4GameRes
{
@ -95,40 +96,6 @@ protected:
void LoadFoldersWithLocalDefs(const char *szPath);
};
// Definitions of custom parameters that can be set before scenario start
class C4ScenarioParameterDefs
{
std::vector<C4ScenarioParameterDef> Parameters;
public:
C4ScenarioParameterDefs() {}
~C4ScenarioParameterDefs() {}
void Clear() { Parameters.clear(); }
const C4ScenarioParameterDef *GetParameterDefByIndex(size_t idx) const;
bool Load(C4Group &hGroup, class C4LangStringTable *pLang);
void CompileFunc(StdCompiler *pComp);
void RegisterScriptConstants(const class C4ScenarioParameters &values); // register constants for all parameters in script engine
};
// Parameter values that correspond to settings offered in C4ScenarioParameterDefs
class C4ScenarioParameters
{
std::map<StdCopyStrBuf, int32_t> Parameters;
public:
C4ScenarioParameters() {}
~C4ScenarioParameters() {}
int32_t GetValueByID(const char *id, int32_t default_value) const;
void SetValue(const char *id, int32_t value);
void CompileFunc(StdCompiler *pComp);
};
class C4GameParameters
{
public:

View File

@ -2468,6 +2468,29 @@ static int32_t FnGetStartupPlayerCount(C4PropList * _this)
return ::Game.StartupPlayerCount;
}
static bool FnGainScenarioAchievement(C4PropList * _this, C4String *achievement_name, Nillable<long> avalue, Nillable<long> player, C4String *for_scenario)
{
// safety
if (!achievement_name || !achievement_name->GetData().getLength()) return false;
// default parameter
long value = avalue.IsNil() ? 1 : avalue;
// gain achievement
bool result = true;
if (!player.IsNil() && player != NO_OWNER)
{
C4Player *plr = ::Players.Get(player);
if (!plr) return false;
result = plr->GainScenarioAchievement(achievement_name->GetCStr(), value, for_scenario ? for_scenario->GetCStr() : NULL);
}
else
{
for (C4Player *plr = ::Players.First; plr; plr = plr->Next)
if (!plr->GainScenarioAchievement(achievement_name->GetCStr(), value, for_scenario ? for_scenario->GetCStr() : NULL))
result = false;
}
return true;
}
extern C4ScriptConstDef C4ScriptGameConstMap[];
extern C4ScriptFnDef C4ScriptGameFnMap[];
@ -2634,6 +2657,7 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "GetStartupPlayerCount", FnGetStartupPlayerCount);
AddFunc(pEngine, "PlayerObjectCommand", FnPlayerObjectCommand);
AddFunc(pEngine, "EditCursor", FnEditCursor);
AddFunc(pEngine, "GainScenarioAchievement", FnGainScenarioAchievement);
F(GetPlrKnowledge);
F(GetComponent);

View File

@ -29,6 +29,8 @@
#include <C4DrawGL.h>
/* C4GraphicsResource */
C4GraphicsResource::C4GraphicsResource():
idSfcCaption(0), idSfcButton(0), idSfcButtonD(0), idSfcScroll(0), idSfcContext(0),
CaptionFont(FontCaption), TitleFont(FontTitle), TextFont(FontRegular), MiniFont(FontTiny), TooltipFont(FontTooltip)
@ -138,6 +140,8 @@ void C4GraphicsResource::Clear()
// unhook deflist from font
FontRegular.SetCustomImages(NULL);
Achievements.Clear();
// closing the group set will also close the graphics.ocg
// this is just for games that failed to init
// normally, this is done after successful init anyway
@ -251,6 +255,9 @@ bool C4GraphicsResource::Init()
if (!LoadFile(fctGamepad, "Gamepad", Files, 80)) return false;
if (!LoadFile(fctBuild, "Build", Files)) return false;
// achievements
if (!Achievements.Init(Files)) return false;
// create ColorByOwner overlay surfaces
if (fctCrew.idSourceGroup != fctCrewClr.idSourceGroup)
{

View File

@ -25,6 +25,7 @@
#include <C4Surface.h>
#include <C4FacetEx.h>
#include <C4Gui.h>
#include <C4ScenarioParameters.h>
class C4GraphicsResource
{
@ -101,6 +102,9 @@ public:
CStdFont FontCaption; // used for title bars
CStdFont FontTitle; // huge font for titles
CStdFont FontTooltip; // normal, non-shadowed font (same as BookFont)
// achievement graphics
C4AchievementGraphics Achievements;
public:
int32_t GetColorIndex(int32_t iColor, bool fLast=false);
CStdFont &GetFontByHeight(int32_t iHgt, float *pfZoom=NULL); // get optimal font for given control size

View File

@ -495,7 +495,7 @@ namespace C4GameLobby
case PID_SetScenarioParameter: // set a scenario parameter value
{
GETPKT(C4PacketSetScenarioParameter, Pkt);
::Game.Parameters.ScenarioParameters.SetValue(Pkt.GetID(), Pkt.GetValue());
::Game.Parameters.ScenarioParameters.SetValue(Pkt.GetID(), Pkt.GetValue(), false);
// reflect updated value immediately on clients
if (pRightTab->GetActiveSheetIndex() == SheetIdx_Options) if (pOptionsList) pOptionsList->Update();
}

View File

@ -103,7 +103,7 @@ void C4GameOptionsList::OptionScenarioParameter::DoDropdownSelChange(int32_t idN
C4GameLobby::C4PacketSetScenarioParameter pck(ParameterDef->GetID(), idNewSelection);
::Network.Clients.BroadcastMsgToClients(MkC4NetIOPacket(PID_SetScenarioParameter, pck));
// also process on host
::Game.Parameters.ScenarioParameters.SetValue(ParameterDef->GetID(), idNewSelection);
::Game.Parameters.ScenarioParameters.SetValue(ParameterDef->GetID(), idNewSelection, false);
}
void C4GameOptionsList::OptionScenarioParameter::Update()
@ -295,7 +295,8 @@ void C4GameOptionsList::InitOptions()
// create options for custom scenario parameters
size_t idx = 0; const C4ScenarioParameterDef *def;
while (def = ::Game.ScenarioParameterDefs.GetParameterDefByIndex(idx++))
new OptionScenarioParameter(this, def);
if (!def->IsAchievement()) // achievements are displayed in scenario selection. no need to repeat them here
new OptionScenarioParameter(this, def);
// creates option selection components
new OptionControlMode(this);
new OptionControlRate(this);

View File

@ -405,7 +405,7 @@ void C4MapFolderData::ResetSelection()
// ------------------------------------
// Entry
C4ScenarioListLoader::Entry::Entry(Folder *pParent) : pNext(NULL), pParent(pParent), fBaseLoaded(false), fExLoaded(false)
C4ScenarioListLoader::Entry::Entry(class C4ScenarioListLoader *pLoader, Folder *pParent) : pLoader(pLoader), pNext(NULL), pParent(pParent), fBaseLoaded(false), fExLoaded(false)
{
// ctor: Put into parent tree node
if (pParent)
@ -550,20 +550,20 @@ bool DirContainsScenarios(const char *szDir)
return !!szChildFilename;
}
C4ScenarioListLoader::Entry *C4ScenarioListLoader::Entry::CreateEntryForFile(const StdStrBuf &sFilename, Folder *pParent)
C4ScenarioListLoader::Entry *C4ScenarioListLoader::Entry::CreateEntryForFile(const StdStrBuf &sFilename, C4ScenarioListLoader *pLoader, Folder *pParent)
{
// determine entry type by file type
const char *szFilename = sFilename.getData();
if (!szFilename || !*szFilename) return NULL;
if (WildcardMatch(C4CFN_ScenarioFiles, sFilename.getData())) return new Scenario(pParent);
if (WildcardMatch(C4CFN_FolderFiles, sFilename.getData())) return new SubFolder(pParent);
if (WildcardMatch(C4CFN_ScenarioFiles, sFilename.getData())) return new Scenario(pLoader, pParent);
if (WildcardMatch(C4CFN_FolderFiles, sFilename.getData())) return new SubFolder(pLoader, pParent);
// regular, open folder (C4Group-packed folders without extensions are not regarded, because they could contain anything!)
const char *szExt = GetExtension(szFilename);
if ((!szExt || !*szExt) && DirectoryExists(sFilename.getData()))
{
// open folders only if they contain a scenario or folder
if (DirContainsScenarios(szFilename))
return new RegularFolder(pParent);
return new RegularFolder(pLoader, pParent);
}
// type not recognized
return NULL;
@ -670,6 +670,39 @@ bool C4ScenarioListLoader::Scenario::LoadCustomPre(C4Group &rGrp)
if (!rGrp.LoadEntryString(C4CFN_ScenarioCore, &sFileContents)) return false;
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(C4S, false), sFileContents, (rGrp.GetFullName() + DirSep C4CFN_ScenarioCore).getData()))
return false;
// ...and localized parameter definitions. needed for achievements and parameter input boxes.
C4LangStringTable ScenarioLangStringTable;
C4Language::LoadComponentHost(&ScenarioLangStringTable, rGrp, C4CFN_ScriptStringTbl, Config.General.LanguageEx);
ParameterDefs.Load(rGrp, &ScenarioLangStringTable);
// achievement images
const C4ScenarioParameterDef *def; size_t idx=0, aidx=0; nAchievements = 0;
while (def = ParameterDefs.GetParameterDefByIndex(idx++))
if (def->IsAchievement())
{
int32_t val = pLoader->GetAchievements().GetValueByID(C4ScenarioParameters::AddFilename2ID(rGrp.GetFullName().getData(), def->GetID()).getData(), def->GetDefault());
if (val)
{
// player has this achievement - find graphics for it
const char *achievement_gfx = def->GetAchievement();
StdStrBuf sAchievementFilename(C4CFN_Achievements);
sAchievementFilename.Replace("*", achievement_gfx);
if (!fctAchievements[aidx].Load(rGrp, sAchievementFilename.getData(), C4FCT_Height, C4FCT_Full))
{
const C4FacetSurface *fct = ::GraphicsResource.Achievements.FindByName(achievement_gfx);
if (!fct) continue; // achievement graphics not found :(
fctAchievements[aidx].Set((const C4Facet &)*fct);
}
// section by achievement index (1-based, since zero means no achievement)
if (val>1) fctAchievements[aidx].Set(fctAchievements[aidx].GetSection(val-1));
// description for this achievement is taken from option
const C4ScenarioParameterDef::Option *opt = def->GetOptionByValue(val);
if (opt) sAchievementDescriptions[aidx] = opt->Description;
// keep track of achievement count
++aidx; ++nAchievements;
if (aidx == C4StartupScenSel_MaxAchievements) break;
;
}
}
return true;
}
@ -694,6 +727,15 @@ bool C4ScenarioListLoader::Scenario::LoadCustom(C4Group &rGrp, bool fNameLoaded,
return true;
}
bool C4ScenarioListLoader::Scenario::GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description)
{
// return true and fill output parameters if player got the indexed achievement
if (idx < 0 || idx >= nAchievements) return false;
*out_facet = fctAchievements[idx];
*out_description = sAchievementDescriptions[idx].getData();
return true;
}
bool C4ScenarioListLoader::Scenario::Start()
{
// gogo!
@ -978,7 +1020,7 @@ bool C4ScenarioListLoader::SubFolder::DoLoadContents(C4ScenarioListLoader *pLoad
sChildFilename.Ref(ChildFilename);
// okay; create this item
//LogF("SubFolder \"%s\" loading \"%s\"", (const char *) sFilename, (const char *) sChildFilename);
Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, this);
Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
if (pNewEntry)
{
// ...and load it
@ -1071,7 +1113,7 @@ bool C4ScenarioListLoader::RegularFolder::DoLoadContents(C4ScenarioListLoader *p
if (names.find(szChildFilename) != names.end()) continue;
names.insert(szChildFilename);
// filename okay; create this item
Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, this);
Entry *pNewEntry = Entry::CreateEntryForFile(sChildFilename, pLoader, this);
if (pNewEntry)
{
// ...and load it
@ -1098,7 +1140,7 @@ void C4ScenarioListLoader::RegularFolder::Merge(const char *szPath)
// ------------------------------------
// C4ScenarioListLoader
C4ScenarioListLoader::C4ScenarioListLoader() : pRootFolder(NULL), pCurrFolder(NULL),
C4ScenarioListLoader::C4ScenarioListLoader(const C4ScenarioParameters &Achievements) : Achievements(Achievements), pRootFolder(NULL), pCurrFolder(NULL),
iLoading(0), iProgress(0), iMaxProgress(0), fAbortThis(false), fAbortPrevious(false)
{
}
@ -1158,7 +1200,7 @@ bool C4ScenarioListLoader::Load(const StdStrBuf &sRootFolder)
// (unthreaded) loading of all entries in root folder
if (!BeginActivity(true)) return false;
if (pRootFolder) { delete pRootFolder; pRootFolder = NULL; }
pCurrFolder = pRootFolder = new RegularFolder(NULL);
pCurrFolder = pRootFolder = new RegularFolder(this, NULL);
// Load regular game data if no explicit path specified
if(!sRootFolder.getData())
for(C4Reloc::iterator iter = Reloc.begin(); iter != Reloc.end(); ++iter)
@ -1238,10 +1280,26 @@ C4StartupScenSelDlg::ScenListItem::ScenListItem(C4GUI::ListBox *pForListBox, C4S
pIcon = new C4GUI::Picture(C4Rect(0, 0, iHeight, iHeight), true);
pIcon->SetFacet(pScenListEntry->GetIconFacet());
pNameLabel = new C4GUI::Label(pScenListEntry->GetName().getData(), iHeight + IconLabelSpacing, IconLabelSpacing, ALeft, fEnabled ? ClrScenarioItem : ClrScenarioItemDisabled, &rUseFont, false, false);
// achievement components
for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i)
{
C4Facet fct; const char *desc;
if (pForEntry->GetAchievement(i, &fct, &desc))
{
ppAchievements[i] = new C4GUI::Picture(C4Rect(iHeight * (i+2), 0, iHeight, iHeight), true); // position will be adjusted later
ppAchievements[i]->SetFacet(fct);
ppAchievements[i]->SetToolTip(desc);
}
else
{
ppAchievements[i] = NULL;
}
}
// calc own bounds - use icon bounds only, because only the height is used when the item is added
SetBounds(pIcon->GetBounds());
// add components
AddElement(pIcon); AddElement(pNameLabel);
for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i) if (ppAchievements[i]) AddElement(ppAchievements[i]);
// tooltip by name, so long names can be read via tooltip
SetToolTip(pScenListEntry->GetName().getData());
// add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
@ -1258,9 +1316,12 @@ void C4StartupScenSelDlg::ScenListItem::UpdateOwnPos()
// parent for client rect
typedef C4GUI::Window ParentClass;
ParentClass::UpdateOwnPos();
// reposition items
// reposition achievement items
C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
// nothing to reposition for now...
for (int32_t i=0; i<C4StartupScenSel_MaxAchievements; ++i) if (ppAchievements[i])
{
ppAchievements[i]->SetBounds(caBounds.GetFromRight(caBounds.GetHeight()));
}
}
void C4StartupScenSelDlg::ScenListItem::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
@ -1453,9 +1514,11 @@ void C4StartupScenSelDlg::DrawElement(C4TargetFacet &cgo)
void C4StartupScenSelDlg::OnShown()
{
C4StartupDlg::OnShown();
// Collect achievements of all activated players
UpdateAchievements();
// init file list
fIsInitialLoading = true;
if (!pScenLoader) pScenLoader = new C4ScenarioListLoader();
if (!pScenLoader) pScenLoader = new C4ScenarioListLoader(Achievements);
pScenLoader->Load(StdStrBuf()); //Config.General.ExePath));
UpdateList();
UpdateSelection();
@ -1840,4 +1903,21 @@ void C4StartupScenSelDlg::AbortRenaming()
if (pRenameEdit) pRenameEdit->Abort();
}
// NICHT: 9, 7.2.2, 113-114
void C4StartupScenSelDlg::UpdateAchievements()
{
// Extract all achievements from activated player files and merge them
Achievements.Clear();
char PlayerFilename[_MAX_FNAME+1];
C4Group PlayerGrp;
for (int i = 0; SCopySegment(Config.General.Participants, i, PlayerFilename, ';', _MAX_FNAME, true); i++)
{
const char *szPlayerFilename = Config.AtUserDataPath(PlayerFilename);
if (!FileExists(szPlayerFilename)) continue;
if (!PlayerGrp.Open(szPlayerFilename)) continue;
C4PlayerInfoCore nfo;
if (!nfo.Load(PlayerGrp)) continue;
Achievements.Merge(nfo.Achievements);
}
}
// NICHT: 9, 7.2.2, 113-114, 8a

View File

@ -21,6 +21,7 @@
#include "C4Startup.h"
#include "C4Scenario.h"
#include "C4Folder.h"
#include "C4ScenarioParameters.h"
#include <list>
#include <string>
@ -36,7 +37,8 @@ const int32_t C4StartupScenSel_DefaultIcon_Scenario = 14,
C4StartupScenSel_TitlePictureWdt = 200,
C4StartupScenSel_TitlePictureHgt = 150,
C4StartupScenSel_TitlePicturePadding = 10,
C4StartupScenSel_TitleOverlayMargin = 10; // number of pixels to each side of title overlay picture
C4StartupScenSel_TitleOverlayMargin = 10, // number of pixels to each side of title overlay picture
C4StartupScenSel_MaxAchievements = 3; // maximum number of achievements shown next to entry
// a list of loaded scenarios
class C4ScenarioListLoader
@ -47,6 +49,7 @@ public:
class Entry
{
protected:
class C4ScenarioListLoader *pLoader;
Entry *pNext;
class Folder *pParent;
@ -61,7 +64,7 @@ public:
int iFolderIndex;
public:
Entry(class Folder *pParent);
Entry(class C4ScenarioListLoader *pLoader, class Folder *pParent);
virtual ~Entry(); // dtor: unlink from tree
bool Load(C4Group *pFromGrp, const StdStrBuf *psFilename, bool fLoadEx); // load as child if pFromGrp, else directly from filename
@ -83,8 +86,9 @@ public:
Entry *GetNext() const { return pNext; }
class Folder *GetParent() const { return pParent; }
virtual StdStrBuf GetTypeName() = 0;
virtual bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description) { return false; } // return true and fill output parameters if player got the indexed achievement
static Entry *CreateEntryForFile(const StdStrBuf &sFilename, Folder *pParent); // create correct entry type based on file extension
static Entry *CreateEntryForFile(const StdStrBuf &sFilename, C4ScenarioListLoader *pLoader, Folder *pParent); // create correct entry type based on file extension
virtual bool CanOpen(StdStrBuf &sError) { return true; } // whether item can be started/opened (e.g. mission access, unregistered)
virtual bool IsGrayed() { return false; } // additional condition for graying out - notice unreg folders are grayed but can still be opened
@ -104,11 +108,15 @@ public:
{
private:
C4Scenario C4S;
C4ScenarioParameterDefs ParameterDefs;
C4FacetSurface fctAchievements[C4StartupScenSel_MaxAchievements];
StdCopyStrBuf sAchievementDescriptions[C4StartupScenSel_MaxAchievements];
int32_t nAchievements;
bool fNoMissionAccess;
int32_t iMinPlrCount;
public:
Scenario(class Folder *pParent) : Entry(pParent), fNoMissionAccess(false), iMinPlrCount(0) {}
Scenario(class C4ScenarioListLoader *pLoader, class Folder *pParent) : Entry(pLoader, pParent), fNoMissionAccess(false), iMinPlrCount(0), nAchievements(0) {}
virtual ~Scenario() {}
virtual bool LoadCustom(C4Group &rGrp, bool fNameLoaded, bool fIconLoaded); // do fallbacks for title and icon; check whether scenario is valid
@ -121,6 +129,7 @@ public:
virtual StdStrBuf GetOpenText(); // get open button text
virtual StdStrBuf GetOpenTooltip();
const C4Scenario &GetC4S() const { return C4S; } // get scenario core
virtual bool GetAchievement(int32_t idx, C4Facet *out_facet, const char **out_description); // return true and fill output parameters if player got the indexed achievement
virtual StdStrBuf GetTypeName() { return StdCopyStrBuf(LoadResStr("IDS_TYPE_SCENARIO"), true); }
@ -140,7 +149,7 @@ public:
friend class Entry;
public:
Folder(Folder *pParent) : Entry(pParent), fContentsLoaded(false), pFirst(NULL), pMapData(NULL) {}
Folder(class C4ScenarioListLoader *pLoader, Folder *pParent) : Entry(pLoader, pParent), fContentsLoaded(false), pFirst(NULL), pMapData(NULL) {}
virtual ~Folder();
virtual bool LoadCustomPre(C4Group &rGrp); // load folder core
@ -171,7 +180,7 @@ public:
class SubFolder : public Folder
{
public:
SubFolder(Folder *pParent) : Folder(pParent) {}
SubFolder(class C4ScenarioListLoader *pLoader, Folder *pParent) : Folder(pLoader, pParent) {}
virtual ~SubFolder() {}
virtual const char *GetDefaultExtension() { return "ocf"; }
@ -187,7 +196,7 @@ public:
class RegularFolder : public Folder
{
public:
RegularFolder(Folder *pParent) : Folder(pParent) {}
RegularFolder(class C4ScenarioListLoader *pLoader, Folder *pParent) : Folder(pLoader, pParent) {}
virtual ~RegularFolder();
virtual StdStrBuf GetTypeName() { return StdCopyStrBuf(LoadResStr("IDS_TYPE_DIRECTORY"), true); }
@ -207,9 +216,10 @@ private:
Folder *pCurrFolder; // scenario list in working directory
int32_t iLoading, iProgress, iMaxProgress;
bool fAbortThis, fAbortPrevious; // activity state
const C4ScenarioParameters &Achievements;
public:
C4ScenarioListLoader();
C4ScenarioListLoader(const C4ScenarioParameters &Achievements);
~C4ScenarioListLoader();
private:
@ -235,6 +245,8 @@ public:
int32_t GetProgress() const { return iProgress; }
int32_t GetMaxProgress() const { return iMaxProgress; }
int32_t GetProgressPercent() const { return iProgress * 100 / Max<int32_t>(iMaxProgress, 1); }
const C4ScenarioParameters &GetAchievements() const { return Achievements; }
};
@ -354,6 +366,7 @@ public:
// subcomponents
C4GUI::Picture *pIcon; // item icon
C4GUI::Label *pNameLabel; // item caption
C4GUI::Picture *ppAchievements[C4StartupScenSel_MaxAchievements]; // achievement icons
C4ScenarioListLoader::Entry *pScenListEntry; // associated, loaded item info
public:
@ -409,6 +422,9 @@ private:
C4GUI::RenameEdit *pRenameEdit;
// achievements of all activated players
C4ScenarioParameters Achievements;
public:
static C4StartupScenSelDlg *pInstance; // singleton
@ -447,6 +463,7 @@ private:
C4ScenarioListLoader::Entry *GetSelectedEntry();
void SetOpenButtonDefaultText();
void FocusScenList();
void UpdateAchievements();
public:
bool StartScenario(C4ScenarioListLoader::Scenario *pStartScen);

View File

@ -221,50 +221,6 @@ public:
void CompileFunc(StdCompiler *pComp);
};
// Definition for a custom setting for the scenario
class C4ScenarioParameterDef
{
public:
// what kind of parameter?
enum ParameterType
{
SPDT_Enum, // only one type so far
};
// single option for an enum type parameter
struct Option
{
int32_t Value; // integer value that will be assigned to the script constant
StdCopyStrBuf Name; // localized name
StdCopyStrBuf Description; // localized description. to be displayed as hover text for this option.
void CompileFunc(StdCompiler *pComp);
};
private:
StdCopyStrBuf Name; // localized name
StdCopyStrBuf Description; // localized description. to be displayed as hover text for this parameter input control
StdCopyStrBuf ID; // Identifier for value storage and script access
ParameterType Type; // Type of parameter. Always enum.
std::vector<Option> Options; // possible options to be selected for an enum type
int32_t Default; // value of option selected by default for an enum type
public:
C4ScenarioParameterDef() : Default(0) {}
~C4ScenarioParameterDef() {}
const char *GetName() const { return Name.getData(); }
const char *GetDescription() const { return Description.getData(); }
const char *GetID() const { return ID.getData(); }
ParameterType GetType() const { return Type; }
int32_t GetDefault() const { return Default; }
const Option *GetOptionByValue(int32_t val) const;
const Option *GetOptionByIndex(size_t idx) const;
void CompileFunc(StdCompiler *pComp);
};
class C4Scenario
{
public:

View File

@ -36,13 +36,20 @@
C4PlayerInfoCore::C4PlayerInfoCore()
{
ZeroMem(this,sizeof(C4PlayerInfoCore));
Default();
}
void C4PlayerInfoCore::Default(C4RankSystem *pRanks)
{
ZeroMem(this,sizeof(C4PlayerInfoCore));
*Comment='\0';
*RankName='\0';
TotalScore=0;
Rounds=RoundsWon=RoundsLost=0;
TotalPlayingTime=0;
*LeagueName='\0';
LastRound.Default();
ExtraData = C4ValueMapData();
PrefControl.Clear();
Rank=0;
SCopy("Neuling",PrefName);
if (pRanks) SCopy(pRanks->GetRankName(Rank,false).getData(),RankName);
@ -166,6 +173,7 @@ void C4PlayerInfoCore::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(LastRound, "LastRound"));
pComp->Value(mkNamingAdapt(Achievements, "Achievements"));
}
//------------------------------- Object Info ----------------------------------------

View File

@ -24,6 +24,7 @@
#include <C4ValueMap.h>
#include "C4Real.h"
#include "C4InputValidation.h"
#include "C4ScenarioParameters.h"
const int32_t C4MaxPhysical = 100000,
C4MaxDeathMsg = 75;
@ -108,6 +109,9 @@ public:
int32_t OldPrefControl;
int32_t OldPrefControlStyle;
int32_t OldPrefAutoContextMenu;
// achievements indexed by achievement name and scenario
C4ScenarioParameters Achievements;
public:
void Default(C4RankSystem *pRanks=NULL);
void Promote(int32_t iRank, C4RankSystem &rRanks);

View File

@ -2028,3 +2028,17 @@ void C4Player::SetViewLocked(bool to_val)
if (ViewMode == C4PVM_Scrolling) SetViewMode(C4PVM_Cursor);
}
}
bool C4Player::GainScenarioAchievement(const char *achievement_id, int32_t value, const char *scen_name_override)
{
// Determine full ID of achievement
if (!scen_name_override)
if (::Game.C4S.Head.Origin.getLength())
scen_name_override = ::Game.C4S.Head.Origin.getData();
else
scen_name_override = ::Game.ScenarioFilename;
StdStrBuf sAchvID = C4ScenarioParameters::AddFilename2ID(scen_name_override, achievement_id);
// Gain achievement iff it's an improvement
Achievements.SetValue(sAchvID.getData(), value, true);
return true;
}

View File

@ -272,6 +272,10 @@ public:
private:
bool AdjustZoomParameter(int32_t *range_par, int32_t new_val, bool no_increase, bool no_decrease);
bool AdjustZoomParameter(C4Fixed *zoom_par, C4Fixed new_val, bool no_increase, bool no_decrease);
public:
// custom scenario achievements
bool GainScenarioAchievement(const char *achievement_id, int32_t value, const char *scen_name_override=NULL);
};
#endif

View File

@ -0,0 +1,271 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2013, 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.
*/
#include "C4Include.h"
#include "C4ScenarioParameters.h"
#include "C4Components.h"
#include "C4Aul.h"
#include "C4FacetEx.h"
/* C4AchievementGraphics */
bool C4AchievementGraphics::Init(C4Group &File)
{
// Load all graphics matching achievement filename and register them to map
char FileName[_MAX_FNAME];
File.ResetSearch();
while (File.FindNextEntry(C4CFN_Achievements, FileName))
{
C4FacetSurface *new_fct = new C4FacetSurface();
if (!new_fct->Load(File, FileName, C4FCT_Height, C4FCT_Full))
{
delete new_fct;
LogF(LoadResStr("IDS_PRC_NOGFXFILE"), FileName, LoadResStr("IDS_ERR_NOFILE"));
return false;
}
// Register under filename excluding the leading "Achv" part. Delete any existing file with same name.
RemoveExtension(FileName);
int32_t id_offset = SCharPos('*', C4CFN_Achievements); assert(id_offset>=0);
StdCopyStrBuf sFileName(FileName + id_offset);
auto i = Graphics.find(sFileName);
if (i != Graphics.end()) delete i->second;
Graphics[sFileName] = new_fct;
}
// done. success no matter how many files were loaded.
return true;
}
bool C4AchievementGraphics::Init(C4GroupSet &Files)
{
int32_t idNewGrp=0;
C4Group *pGrp = Files.FindEntry(C4CFN_Achievements, NULL, &idNewGrp);
if (!pGrp) return true; // no achievement gfx. That's OK.
if (idNewGrp == idGrp) return true; // no update
idGrp = idNewGrp;
// OK, load from this group
return Init(*pGrp);
}
void C4AchievementGraphics::Clear()
{
for (auto i = Graphics.begin(); i != Graphics.end(); ++i)
delete i->second;
Graphics.clear();
idGrp = 0;
}
C4FacetSurface *C4AchievementGraphics::FindByName(const char *name) const
{
auto i = Graphics.find(StdCopyStrBuf(name));
if (i != Graphics.end()) return i->second; else return NULL;
}
// *** C4ScenarioParameters
void C4ScenarioParameterDef::Option::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("Option")) { pComp->NameEnd(); pComp->excNotFound("Option"); }
pComp->Value(mkNamingAdapt(mkParAdapt(Name, StdCompiler::RCT_All), "Name", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkParAdapt(Description, StdCompiler::RCT_All), "Description", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt( Value, "Value", 0));
pComp->NameEnd();
}
const C4ScenarioParameterDef::Option *C4ScenarioParameterDef::GetOptionByValue(int32_t val) const
{
// search option by value
for (auto i = Options.cbegin(); i != Options.cend(); ++i)
if (i->Value == val)
return &*i;
return NULL;
}
const C4ScenarioParameterDef::Option *C4ScenarioParameterDef::GetOptionByIndex(size_t idx) const
{
if (idx >= Options.size()) return NULL;
return &Options[idx];
}
void C4ScenarioParameterDef::CompileFunc(StdCompiler *pComp)
{
if (!pComp->Name("ParameterDef")) { pComp->NameEnd(); pComp->excNotFound("ParameterDef"); }
pComp->Value(mkNamingAdapt(mkParAdapt(Name, StdCompiler::RCT_All), "Name", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkParAdapt(Description, StdCompiler::RCT_All), "Description", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkParAdapt(ID, StdCompiler::RCT_Idtf), "ID", StdCopyStrBuf()));
StdEnumEntry<ParameterType> ParTypeEntries[] =
{
{ "Enumeration", SPDT_Enum },
{ NULL, SPDT_Enum }
};
pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(Type, ParTypeEntries), "Type", SPDT_Enum));
pComp->Value(mkNamingAdapt(Default, "Default", 0));
pComp->Value(mkNamingAdapt(mkParAdapt(Achievement, StdCompiler::RCT_Idtf), "Achievement", StdCopyStrBuf()));
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Options, StdCompiler::SEP_NONE), "Options"));
pComp->NameEnd();
}
void C4ScenarioParameterDefs::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkSTLContainerAdapt(Parameters, StdCompiler::SEP_NONE));
}
const C4ScenarioParameterDef *C4ScenarioParameterDefs::GetParameterDefByIndex(size_t idx) const
{
if (idx >= Parameters.size()) return NULL;
return &Parameters[idx];
}
bool C4ScenarioParameterDefs::Load(C4Group &hGroup, C4LangStringTable *pLang)
{
// Load buffer, localize and parse
StdStrBuf Buf;
if (!hGroup.LoadEntryString(C4CFN_ScenarioParameterDefs,&Buf)) return false;
if (pLang) pLang->ReplaceStrings(Buf);
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Buf, C4CFN_ScenarioCore))
{ return false; }
return true;
}
void C4ScenarioParameterDefs::RegisterScriptConstants(const C4ScenarioParameters &values)
{
// register constants for all parameters in script engine
for (auto i = Parameters.cbegin(); i != Parameters.cend(); ++i)
{
StdStrBuf constant_name;
constant_name.Format("SCENPAR_%s", i->GetID());
int32_t constant_value = values.GetValueByID(i->GetID(), i->GetDefault());
::ScriptEngine.RegisterGlobalConstant(constant_name.getData(), C4VInt(constant_value));
}
}
void C4ScenarioParameters::Clear()
{
Parameters.clear();
}
void C4ScenarioParameters::Merge(const C4ScenarioParameters &other)
{
// Merge lists and keep larger value
for (auto i = other.Parameters.cbegin(); i != other.Parameters.cend(); ++i)
{
auto j = Parameters.find(i->first);
if (j != Parameters.end())
if (j->second >= i->second)
continue; // existing value is same or larger - keep old
// update to new value from other list
Parameters[i->first] = i->second;
}
}
int32_t C4ScenarioParameters::GetValueByID(const char *id, int32_t default_value) const
{
// return map value if name is in map. Otherwise, return default value.
auto i = Parameters.find(StdStrBuf(id));
if (i != Parameters.end()) return i->second; else return default_value;
}
void C4ScenarioParameters::SetValue(const char *id, int32_t value, bool only_if_larger)
{
if (only_if_larger)
{
auto i = Parameters.find(StdStrBuf(id));
if (i != Parameters.end())
if (i->second >= value)
// could become smaller. don't set.
return;
}
// just update map
Parameters[StdCopyStrBuf(id)] = value;
}
void C4ScenarioParameters::CompileFunc(StdCompiler *pComp)
{
// Unfortunately, StdCompiler cannot save std::map yet
if (pComp->isCompiler())
{
Parameters.clear();
if (pComp->hasNaming())
{
// load from INI
size_t name_count = pComp->NameCount();
for (size_t i=0; i<name_count; ++i)
{
int32_t v=0;
const char *name = pComp->GetNameByIndex(0); // always get name index 0, because names are removed after values have been extracted for them
StdCopyStrBuf sName(name);
if (!name) continue;
pComp->Value(mkNamingAdapt(v, sName.getData(), 0));
Parameters[sName] = v;
}
}
else
{
// load from binary
int32_t name_count=0;
pComp->Value(name_count);
for (int32_t i=0; i<name_count; ++i)
{
StdCopyStrBuf name; int32_t v;
pComp->Value(name);
pComp->Value(v);
Parameters[name] = v;
}
}
}
else
{
if (pComp->hasNaming())
{
// save to INI
for (auto i = Parameters.begin(); i != Parameters.end(); ++i)
pComp->Value(mkNamingAdapt(i->second, i->first.getData()));
}
else
{
// save to binary
int32_t name_count=Parameters.size();
pComp->Value(name_count);
for (auto i = Parameters.begin(); i != Parameters.end(); ++i)
{
pComp->Value(const_cast<StdCopyStrBuf &>(i->first));
pComp->Value(i->second);
}
}
}
}
StdStrBuf C4ScenarioParameters::AddFilename2ID(const char *filename, const char *id)
{
// composes an ID string that contains both the relevant part of the filename and the ID
// we care for .oc* folders only
StdStrBuf sResult, sSource(filename, true), sPart;
sSource.ReplaceChar(AltDirectorySeparator, DirectorySeparator);
size_t idx=0;
while (sSource.GetSection(idx++, &sPart, DirectorySeparator))
{
size_t len = sPart.getLength();
if (len > 4 && !strnicmp(sPart.getPtr(len - 4), ".oc", 3))
{
// .oc* folders separated by underscores
sResult.Append(sPart.getData(), len - 4);
sResult.AppendChar('_');
}
}
sResult.Append(id);
return sResult;
}

View File

@ -0,0 +1,130 @@
/*
* 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-2013, 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.
*/
/* Structures for object and player info components */
#ifndef INC_C4ScenarioParameters
#define INC_C4ScenarioParameters
// Maps IDs to achievement graphics to be shown beside scenarios (and maybe other things)
class C4AchievementGraphics
{
std::map<StdCopyStrBuf, C4FacetSurface *> Graphics;
int32_t idGrp; // ID of group file from which achievements were loaded
public:
C4AchievementGraphics() : idGrp(0) {}
// Init will always load all achievement files from the first group that contains achievements
bool Init(C4Group &File);
bool Init(C4GroupSet &Files);
void Clear();
C4FacetSurface *FindByName(const char *name) const;
};
// Definition for a custom setting for the scenario
class C4ScenarioParameterDef
{
public:
// what kind of parameter?
enum ParameterType
{
SPDT_Enum, // only one type so far
};
// single option for an enum type parameter
struct Option
{
int32_t Value; // integer value that will be assigned to the script constant
StdCopyStrBuf Name; // localized name
StdCopyStrBuf Description; // localized description. to be displayed as hover text for this option.
void CompileFunc(StdCompiler *pComp);
};
private:
StdCopyStrBuf Name; // localized name
StdCopyStrBuf Description; // localized description. to be displayed as hover text for this parameter input control
StdCopyStrBuf ID; // Identifier for value storage and script access
ParameterType Type; // Type of parameter. Always enum.
std::vector<Option> Options; // possible options to be selected for an enum type
int32_t Default; // value of option selected by default for an enum type
StdCopyStrBuf Achievement; // if this parameter is an achievement, this string contains the name of the achievement graphics to be used
public:
C4ScenarioParameterDef() : Default(0) {}
~C4ScenarioParameterDef() {}
const char *GetName() const { return Name.getData(); }
const char *GetDescription() const { return Description.getData(); }
const char *GetID() const { return ID.getData(); }
ParameterType GetType() const { return Type; }
int32_t GetDefault() const { return Default; }
const Option *GetOptionByValue(int32_t val) const;
const Option *GetOptionByIndex(size_t idx) const;
bool IsAchievement() const { return Achievement.getLength()>0; }
const char *GetAchievement() const { return Achievement.getData(); }
void CompileFunc(StdCompiler *pComp);
};
// Definitions of custom parameters that can be set before scenario start
class C4ScenarioParameterDefs
{
std::vector<C4ScenarioParameterDef> Parameters;
public:
C4ScenarioParameterDefs() {}
~C4ScenarioParameterDefs() {}
void Clear() { Parameters.clear(); }
const C4ScenarioParameterDef *GetParameterDefByIndex(size_t idx) const;
bool Load(C4Group &hGroup, class C4LangStringTable *pLang);
void CompileFunc(StdCompiler *pComp);
void RegisterScriptConstants(const class C4ScenarioParameters &values); // register constants for all parameters in script engine
};
// Parameter values that correspond to settings offered in C4ScenarioParameterDefs
class C4ScenarioParameters
{
std::map<StdCopyStrBuf, int32_t> Parameters;
public:
C4ScenarioParameters() {}
~C4ScenarioParameters() {}
void Clear();
void Merge(const C4ScenarioParameters &other);
int32_t GetValueByID(const char *id, int32_t default_value) const;
void SetValue(const char *id, int32_t value, bool only_if_larger);
void CompileFunc(StdCompiler *pComp);
static StdStrBuf AddFilename2ID(const char *filename, const char *id);
};
#endif // INC_C4ScenarioParameters