Added Enigma game

This commit is contained in:
pelya
2010-10-13 17:30:44 +03:00
parent 8bd2d39dfe
commit bf7d3f22c6
308 changed files with 92986 additions and 39 deletions

View File

@@ -0,0 +1,720 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "lev/Index.hh"
#include "lev/VolatileIndex.hh"
#include "errors.hh"
#include "main.hh"
#include "options.hh"
#include "sound.hh"
#include "PreferenceManager.hh"
#include "StateManager.hh"
#include "lev/ScoreManager.hh"
#include "lev/RatingManager.hh"
#include <cstdio>
namespace enigma { namespace lev {
std::map<std::string, Index *> Index::indices;
std::map<std::string, std::vector<Index *> *> Index::indexGroups;
Index * Index::currentIndex = NULL;
std::string Index::currentGroup;
std::map<std::string, std::string> Index::nullExtensions;
void Index::initGroups() {
ASSERT(indexGroups.empty(), XFrontend, "Reinitialization of groups");
std::vector<std::string> groupNames = getGroupNames();
for (int i = 0; i < groupNames.size(); i++) {
std::vector<Index *> *group = new std::vector<Index *>;
indexGroups.insert(std::make_pair(groupNames[i], group));
}
currentGroup = app.state->getString("CurrentGroup");
}
void Index::registerIndex(Index *anIndex) {
if (anIndex == NULL)
return;
// check for uniqueness of index name
if (findIndex(anIndex->getName()) != NULL)
return;
indices.insert(std::make_pair(anIndex->getName(), anIndex));
// register index in state.xml and update current position, first with last values
std::string groupName = "";
double stateLocation = 0;
app.state->addIndex(anIndex->getName(), groupName, stateLocation,
anIndex->currentPosition, anIndex->screenFirstPosition);
// user location for index?
if (stateLocation > 0)
anIndex->indexLocation = stateLocation;
// reset positions that are out of range - this may happen due to
// modified levelpacks (updates, deleted levels in auto, new commandline)
if (anIndex->currentPosition < 0 || anIndex->currentPosition >= anIndex->size())
anIndex->currentPosition = 0;
// check user preferences for assigned group
if (!groupName.empty())
anIndex->indexGroup = groupName; // use users preference
else
groupName = anIndex->indexGroup; // use index default group
std::vector<Index *> * group;
// if no prefs ask for index default group
// make new group if not existing
if (groupName != INDEX_EVERY_GROUP) {
std::map<std::string, std::vector<Index *> *>::iterator i = indexGroups.find(groupName);
if (i != indexGroups.end()) {
group = i->second;
} else {
// make the group
group = new std::vector<Index *>;
indexGroups.insert(std::make_pair(groupName, group));
app.state->addGroup(groupName, anIndex->getName(), 0);
// fill group with indices that appear in every group
std::map<std::string, Index *>::iterator iti;
for (iti = indices.begin(); iti != indices.end(); iti++)
if ((*iti).second->getGroupName() == INDEX_EVERY_GROUP)
addIndexToGroup((*iti).second, group);
}
}
if (groupName != INDEX_EVERY_GROUP) {
// insert according to user prefs or index defaults
addIndexToGroup(anIndex, group);
addIndexToGroup(anIndex, getGroup(INDEX_ALL_PACKS));
} else {
// add index to all groups inclusive INDEX_ALL_PACKS
std::map<std::string, std::vector<Index *> *>::iterator itg;
for (itg = indexGroups.begin(); itg != indexGroups.end(); itg++)
addIndexToGroup(anIndex, (*itg).second);
}
return;
}
void Index::addIndexToGroup(Index *anIndex, std::vector<Index *> * aGroup) {
std::vector<Index *>::iterator itg;
for (itg = aGroup->begin(); itg != aGroup->end() &&
(*itg)->indexLocation <= anIndex->indexLocation;
itg++) {
}
aGroup->insert(itg, anIndex);
}
void Index::removeIndexFromGroup(Index *anIndex, std::string groupName) {
std::vector<Index *> *theGroup = indexGroups[groupName];
std::vector<Index *>::iterator itg;
for (itg = theGroup->begin(); itg != theGroup->end(); itg++) {
if ((*itg) == anIndex) {
theGroup->erase(itg);
return;
}
}
}
Index * Index::findIndex(std::string anIndexName) {
std::string::size_type lastChar = anIndexName.find_last_not_of(" ");
if (lastChar == std::string::npos)
// the name is effectively an empty string
return NULL;
// stip of trailing and leading spaces
std::string name = anIndexName.substr(0 , lastChar + 1);
name = name.substr(anIndexName.find_first_not_of(" "));
std::map<std::string, Index *>::iterator i = indices.find(name);
if (i != indices.end())
return i->second;
else
return NULL;
}
std::string Index::getCurrentGroup() {
if (currentIndex == NULL)
// initialize current group
getCurrentIndex();
return currentGroup;
}
void Index::setCurrentGroup(std::string groupName) {
// set group - even "All Packs"
app.state->setProperty("CurrentGroup", groupName);
currentGroup = groupName;
// set current index for desired group
std::string indexName = getGroupSelectedIndex(groupName);
Index * newIndex = findIndex(indexName);
std::string indexGroupName;
if (newIndex != NULL)
indexGroupName = newIndex->getGroupName();
if (newIndex != NULL && (indexGroupName == groupName ||
indexGroupName == INDEX_EVERY_GROUP ||
groupName == INDEX_ALL_PACKS)) {
// set the groups current index as main current index
setCurrentIndex(indexName);
} else {
// the groups current index is no longer available or did change the
// group -- reset the groups current index
std::vector<Index *> * group = getGroup(groupName);
if (group->size() > 0) {
setCurrentIndex((*group)[0]->getName());
} else {
// the group is empty -- delete group current index entry and
// leave the apps current index unchanged
setGroupSelectedIndex(groupName,"");
}
}
// Log << "Index setCurrentGroup: wanted " << groupName << " - got " << currentGroup << " - idxGroup " << indexGroupName <<"\n";
}
std::vector<std::string> Index::getGroupNames() {
std::vector<std::string> names;
app.state->getGroupNames(&names);
return names;
}
void Index::deleteGroup(std::string groupName) {
std::vector<Index *> * theGroup = getGroup(groupName);
if (theGroup != NULL) {
indexGroups.erase(groupName);
delete theGroup;
}
if (currentGroup == groupName) {
std::vector<std::string> groups = getGroupNames();
for (int i = 0; i < groups.size(); i++) {
if (groups[i] == groupName) {
if (i > 0) {
setCurrentGroup(groups[i-1]);
} else {
ASSERT (groups.size() > 1, XFrontend, "Delete of last existing group.");
setCurrentGroup(groups[1]);
}
break;
}
}
}
app.state->deleteGroup(groupName);
}
void Index::moveGroup(std::string groupName, int newPos) {
std::string indexName = app.state->getGroupSelectedIndex(groupName);
std::string column = app.state->getGroupSelectedColumn(groupName);
app.state->deleteGroup(groupName);
app.state->insertGroup(newPos, groupName, indexName, column);
}
void Index::renameGroup(std::string oldName, std::string newName) {
// rename state group element
app.state->renameGroup(oldName, newName);
// rename map of groups
std::vector<Index *> * group = getGroup(oldName);
indexGroups.erase(oldName);
indexGroups.insert(std::make_pair(newName, group));
// rename group name in indices
for (int i = 0; i < group->size(); i++) {
if ((*group)[i]->getGroupName() == oldName) {
(*group)[i]->indexGroup = newName;
// set group as users choice for index in state
app.state->setIndexGroup((*group)[i]->getName(), newName);
}
}
// handle currentGroup
if (currentGroup == oldName) {
currentGroup = newName;
app.state->setProperty("CurrentGroup", newName);
}
}
void Index::insertGroup(std::string groupName, int newPos) {
// make the group
std::vector<Index *> *group = new std::vector<Index *>;
indexGroups.insert(std::make_pair(groupName, group));
app.state->insertGroup(newPos, groupName, "", "");
// fill group with indices that appear in every group
std::map<std::string, Index *>::iterator iti;
for (iti = indices.begin(); iti != indices.end(); iti++)
if ((*iti).second->getGroupName() == INDEX_EVERY_GROUP)
addIndexToGroup((*iti).second, group);
setCurrentGroup(groupName);
}
std::string Index::getGroupSelectedIndex(std::string groupName) {
return app.state->getGroupSelectedIndex(groupName);
}
int Index::getGroupSelectedColumn(std::string groupName) {
std::string columnString = app.state->getGroupSelectedColumn(groupName);
if (columnString.empty())
return INDEX_GROUP_COLUMN_UNKNOWN;
else {
int col = INDEX_GROUP_COLUMN_UNKNOWN;
std::sscanf(columnString.c_str(), "%i", &col);
return col;
}
}
void Index::setGroupSelectedIndex(std::string groupName, std::string indexName) {
app.state->setGroupSelectedIndex(groupName, indexName);
}
void Index::setGroupSelectedColumn(std::string groupName, int column) {
if (column == INDEX_GROUP_COLUMN_UNKNOWN)
app.state->setGroupSelectedColumn(groupName, "");
else
app.state->setGroupSelectedColumn(groupName, ecl::strf("%d",column));
}
Index * Index::getCurrentIndex() {
if (currentIndex == NULL) {
// first look for user preference
if (setCurrentIndex(app.state->getGroupSelectedIndex(
app.state->getString("CurrentGroup"))))
;
// fallback to "Tutorial" pack
else if (setCurrentIndex("Tutorial"))
;
// fallback to any pack
else if (!indices.empty()) {
setCurrentIndex(indices.begin()->second->getName());
}
// add empty pack
else {
std::vector<std::string> emptyList;
registerIndex(new lev::VolatileIndex("Empty Index",
INDEX_DEFAULT_GROUP, emptyList));
setCurrentIndex("Empty Index");
}
}
return currentIndex;
}
bool Index::setCurrentIndex(std::string anIndexName) {
Index * newIndex = findIndex(anIndexName);
if (newIndex != NULL) {
if (newIndex != currentIndex) {
sound::SetDefaultSoundSet(newIndex->get_default_SoundSet());
currentIndex = newIndex;
std::string group = currentIndex->getGroupName();
if (group != INDEX_EVERY_GROUP &&
app.state->getString("CurrentGroup") != INDEX_ALL_PACKS) {
app.state->setProperty("CurrentGroup", group);
currentGroup = group;
}
if (getGroupSelectedIndex(currentGroup) != currentIndex->getName()) {
setGroupSelectedIndex(currentGroup, currentIndex->getName());
setGroupSelectedColumn(currentGroup, INDEX_GROUP_COLUMN_UNKNOWN);
}
}
return true;
}
return false;
}
Index * Index::nextGroupIndex() {
std::vector<Index *> * curGroup = getGroup(currentGroup);
ASSERT(curGroup != NULL, XFrontend, "");
for (int i = 0; i < curGroup->size() - 1; i++) {
if ((*curGroup)[i] == currentIndex)
return (*curGroup)[i+1];
}
return currentIndex;
}
Index * Index::previousGroupIndex() {
std::vector<Index *> * curGroup = getGroup(currentGroup);
ASSERT(curGroup != NULL, XFrontend, "");
for (int i = 1; i < curGroup->size(); i++) {
if ((*curGroup)[i] == currentIndex)
return (*curGroup)[i-1];
}
return currentIndex;
}
Proxy * Index::getCurrentProxy() {
return getCurrentIndex()->getCurrent();
}
std::vector<Index *> * Index::getGroup(std::string groupName) {
std::map<std::string, std::vector<Index *> *>::iterator i = indexGroups.find(groupName);
if (i != indexGroups.end())
return i->second;
else
return NULL;
}
double Index::getNextUserLocation() {
double lastUsed = INDEX_USER_PACK_LOCATION;
std::map<std::string, Index *>::iterator iti;
for (iti = indices.begin(); iti != indices.end(); iti++) {
double idxLocation = (*iti).second->indexLocation;
if (idxLocation > lastUsed && idxLocation < INDEX_DEFAULT_PACK_LOCATION) {
lastUsed = idxLocation;
}
}
if (lastUsed + 999 < INDEX_DEFAULT_PACK_LOCATION)
return lastUsed + 100;
else
return 0.9 * lastUsed + INDEX_DEFAULT_PACK_LOCATION / 10;
}
Index::Index(std::string anIndexName, std::string aGroupName, double defaultLocation) :
indexName (anIndexName), indexGroup (aGroupName), defaultGroup (aGroupName),
indexLocation (defaultLocation), indexDefaultLocation (defaultLocation),
currentPosition (0), screenFirstPosition (0) {
}
Index::~Index() {}
std::string Index::getName() {
return indexName;
}
std::string Index::getGroupName() {
return indexGroup;
}
std::string Index::getDefaultGroupName() {
return defaultGroup;
}
double Index::getLocation() {
return indexLocation;
}
double Index::getDefaultLocation() {
return indexDefaultLocation;
}
void Index::setDefaultLocation(double defLocation) {
indexDefaultLocation = defLocation;
}
void Index::moveToGroup(std::string newGroupName) {
// remove from old group
if (indexGroup != INDEX_EVERY_GROUP) {
// remove index from the unique group
removeIndexFromGroup(this, indexGroup);
removeIndexFromGroup(this, INDEX_ALL_PACKS);
} else {
// remove index from all groups inclusive INDEX_ALL_PACKS
std::vector<std::string> groupNames = getGroupNames();
for (int i = 0; i < groupNames.size(); i++)
removeIndexFromGroup(this, groupNames[i]);
// declare this index as not belonging to any group
indexGroup = "";
}
// create group if not existing
if (newGroupName != INDEX_EVERY_GROUP) {
std::map<std::string, std::vector<Index *> *>::iterator i = indexGroups.find(newGroupName);
if (i == indexGroups.end()) {
// make the group
std::vector<Index *> *group = new std::vector<Index *>;
indexGroups.insert(std::make_pair(newGroupName, group));
app.state->addGroup(newGroupName, indexName, 0);
// fill group with indices that appear in every group
std::map<std::string, Index *>::iterator iti;
for (iti = indices.begin(); iti != indices.end(); iti++)
if ((*iti).second->getGroupName() == INDEX_EVERY_GROUP)
addIndexToGroup((*iti).second, group);
}
}
indexGroup = newGroupName;
// add to new group
if (newGroupName != INDEX_EVERY_GROUP) {
// insert according to user prefs or index defaults
addIndexToGroup(this, getGroup(newGroupName));
addIndexToGroup(this, getGroup(INDEX_ALL_PACKS));
} else {
// add index to all groups inclusive INDEX_ALL_PACKS
std::map<std::string, std::vector<Index *> *>::iterator itg;
for (itg = indexGroups.begin(); itg != indexGroups.end(); itg++)
addIndexToGroup(this, (*itg).second);
}
// store new group as users state
app.state->setIndexGroup(indexName,
newGroupName == defaultGroup ? "" : newGroupName);
// select this index with its new group if it is the current Index
if (this == currentIndex) {
if (indexGroup != INDEX_EVERY_GROUP &&
app.state->getString("CurrentGroup") != INDEX_ALL_PACKS) {
app.state->setProperty("CurrentGroup", indexGroup);
currentGroup = indexGroup;
}
if (getGroupSelectedIndex(currentGroup) != currentIndex->getName()) {
setGroupSelectedIndex(currentGroup, currentIndex->getName());
setGroupSelectedColumn(currentGroup, INDEX_GROUP_COLUMN_UNKNOWN);
}
}
}
int indexLocationCompare(Index * first, Index * second) {
return first->getLocation() < second->getLocation();
}
void Index::locateBehind(std::string predName) {
double predLocation = 0;
double succLocation = 0;
double newLocation;
std::string predGroup;
std::string succGroup;
std::vector<Index *> * allGroup = getGroup(INDEX_ALL_PACKS);
if (predName.empty()) {
succLocation = (*allGroup)[0]->indexLocation;
succGroup = (*allGroup)[0]->indexGroup;
} else {
for (int i = 0; i < allGroup->size(); i++) {
if ((*allGroup)[i]->getName() == predName) {
predLocation = (*allGroup)[i]->indexLocation;
predGroup = (*allGroup)[i]->indexGroup;
int succ = 0;
if ((i+1 < allGroup->size()) && ((*allGroup)[i+1] != this)) {
succ = i + 1;
} else if (i+2 < allGroup->size()) {
succ = i + 2;
}
if (succ > 0) {
succLocation = (*allGroup)[succ]->indexLocation;
succGroup = (*allGroup)[succ]->indexGroup;
}
break;
}
}
}
ASSERT (!(predLocation == 0 && succLocation == 0), XFrontend, "");
if (predLocation == 0) {
if (succGroup == indexGroup) {
newLocation = succLocation * 0.98;
} else {
newLocation = succLocation * 0.75;
}
} else if (succLocation == 0) {
if (predGroup == indexGroup) {
newLocation = predLocation + 100;
} else {
newLocation = predLocation + 10000;
}
} else if ((predGroup == indexGroup && succGroup == indexGroup) ||
(predGroup != indexGroup && succGroup != indexGroup)){
newLocation = (predLocation + succLocation) / 2;
} else if (predGroup == indexGroup) {
newLocation = 0.95 * predLocation + 0.05 * succLocation;
} else {
newLocation = 0.05 * predLocation + 0.95 * succLocation;
}
// Log << "newLocation " << newLocation << "\n";
indexLocation = newLocation;
app.state->setIndexLocation(indexName, indexLocation);
// reorder all groups according to new location
std::map<std::string, std::vector<Index *> *>::iterator itg;
for (itg = indexGroups.begin(); itg != indexGroups.end(); itg++)
std::sort((*itg).second->begin(), (*itg).second->end(), indexLocationCompare);
}
void Index::renameIndex(std::string newName) {
indices.erase(indexName);
indices[newName] = this;
app.state->setIndexName(indexName, newName);
indexName = newName;
}
bool Index::isSource(Proxy *) {
return false;
}
int Index::getCurrentPosition() {
return currentPosition;
}
int Index::getCurrentLevel() {
return currentPosition + 1;
}
Proxy * Index::getCurrent() {
return getProxy(currentPosition);
}
void Index::setCurrentPosition(int newPos) {
// reset positions that are out of range - this may happen due to
// editable Indices
if (newPos < 0 || newPos > size())
newPos = 0;
//
currentPosition = newPos;
app.state->setIndexCurpos(getName(), currentPosition);
}
int Index::getScreenFirstPosition() {
return screenFirstPosition;
}
void Index::setScreenFirstPosition(int iFirstPos) {
screenFirstPosition = iFirstPos;
app.state->setIndexCurfirst(getName(), screenFirstPosition);
}
bool Index::mayPlayLevel(int levelNumber) {
return true;
}
Proxy * Index::getProxy(int pos) {
if (pos >= 0 && pos < proxies.size())
return proxies[pos];
else
return NULL;
}
bool Index::containsProxy(Proxy * aProxy) {
for (int i = 0; i < proxies.size(); i++) {
if (proxies[i] == aProxy)
return true;
}
return false;
}
bool Index::hasNormLevelPath(std::string path) {
for (int i = 0; i < proxies.size(); i++) {
if (proxies[i]->getNormLevelPath() == path)
return true;
}
return false;
}
bool Index::advanceLevel(LevelAdvanceMode advMode) {
NextLevelMode nextMode = static_cast<NextLevelMode>(app.state->getInt("NextLevelMode"));
switch (advMode) {
case ADVANCE_STRICTLY:
nextMode = NEXT_LEVEL_STRICTLY;
break;
case ADVANCE_UNSOLVED:
nextMode = NEXT_LEVEL_UNSOLVED;
break;
default:
break;
};
bool found = false;
const int max = size();
int newPos = currentPosition;
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
while (newPos < max - 1 && !found) {
++newPos;
if (nextMode == NEXT_LEVEL_UNSOLVED || nextMode == NEXT_LEVEL_NOT_BEST ||
nextMode == NEXT_LEVEL_OVER_PAR) {
bool solved = scm->isSolved(proxies[newPos], difficulty);
if (!solved) // always play unsolved levels
found = true;
else { // solved levels
if (nextMode == NEXT_LEVEL_NOT_BEST) {
int par_time = ratingMgr->getBestScore(proxies[newPos], difficulty);
int best_user_time = scm->getBestUserScore(proxies[newPos], difficulty);
if (best_user_time<0 || (par_time>0 && best_user_time>par_time))
found = true;
} else if (nextMode == NEXT_LEVEL_OVER_PAR) {
int par_time = ratingMgr->getParScore(proxies[newPos], difficulty);
int best_user_time = scm->getBestUserScore(proxies[newPos], difficulty);
if (best_user_time<0 || (par_time>0 && best_user_time>par_time))
found = true;
}
}
}
else
found = true;
}
if (!found)
newPos = 0; // ?
currentPosition = newPos;
return found;
}
int Index::size() const {
return proxies.size();
}
void Index::appendProxy(Proxy * newLevel, controlType varCtrl,
scoreUnitType varUnit, std::string varTarget,
std::map<std::string, std::string> varExtensions) {
proxies.push_back(newLevel);
}
void Index::clear() {
// proxies.clear();
}
void Index::updateFromProxies() {
for (int i = 0, l = proxies.size(); i < l; i++) {
try {
proxies[i]->loadMetadata(true);
} catch (XLevelLoading &err) {
// silently ignore errors
}
}
}
/* ---------- LevelPack interface ---------- */
/*! Return the default SoundSet (see options::SoundSet for meaning) */
const char* Index::get_default_SoundSet() const {
return "Enigma";
}
/*! Returns true if it's a twoplayer levelpack, but has no
it-yinyang (needed to add it-yinyang to inventory if
oxyd-linkgame is played as single-player) */
bool Index::needs_twoplayers() const {
return false;
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LEV_INDEX_HH_INCLUDED
#define LEV_INDEX_HH_INCLUDED
#include "lev/Proxy.hh"
#include <map>
#include <string>
#include <vector>
#define INDEX_DEFAULT_GROUP "User"
#define INDEX_EVERY_GROUP "Every Group"
#define INDEX_ALL_PACKS "All Packs"
#define INDEX_STARTUP_PACK_NAME "Startup Levels"
#define INDEX_AUTO_PACK_NAME "Auto Folder"
#define INDEX_HISTORY_PACK_NAME "History"
#define INDEX_SEARCH_PACK_NAME "Search Result"
#define INDEX_CLIPBOARD_PACK_NAME "Clipboard"
#define INDEX_STARTUP_PACK_LOCATION 5100
#define INDEX_AUTO_PACK_LOCATION 5200
#define INDEX_HISTORY_PACK_LOCATION 5300
#define INDEX_SEARCH_PACK_LOCATION 5400
#define INDEX_CLIPBOARD_PACK_LOCATION 5500
#define INDEX_USER_PACK_LOCATION 50000
#define INDEX_DEFAULT_PACK_LOCATION 69000
#define INDEX_GROUP_COLUMN_UNKNOWN -1000
namespace enigma { namespace lev {
enum LevelAdvanceMode {
ADVANCE_NEXT_MODE, // honor NextLevelMode
ADVANCE_STRICTLY, // move to the next level in index
ADVANCE_UNSOLVED // move to next not yet solved level
};
enum NextLevelMode {
NEXT_LEVEL_STRICTLY, // move to the next level in index
NEXT_LEVEL_UNSOLVED, // move to next not yet solved level
NEXT_LEVEL_NOT_BEST, // move to next level where player is not best score holder
NEXT_LEVEL_OVER_PAR // move to next level with a score over PAR
};
/**
*
*/
class Index {
public:
static void initGroups();
static void registerIndex(Index *anIndex);
static Index * findIndex(std::string anIndexName);
static Index * getCurrentIndex();
static bool setCurrentIndex(std::string anIndexName);
static Index * nextGroupIndex();
static Index * previousGroupIndex();
static Proxy * getCurrentProxy();
static std::string getCurrentGroup();
static void setCurrentGroup(std::string groupName);
static std::vector<std::string> getGroupNames();
static std::vector<Index *> * getGroup(std::string groupName);
static std::string getGroupSelectedIndex(std::string groupName);
static int getGroupSelectedColumn(std::string groupName);
static void setGroupSelectedIndex(std::string groupName, std::string indexName);
static void setGroupSelectedColumn(std::string groupName, int column);
static void deleteGroup(std::string groupName);
static void moveGroup(std::string groupName, int newPos);
static void renameGroup(std::string oldName, std::string newName);
static void insertGroup(std::string groupName, int newPos);
static double getNextUserLocation();
/**
* Convention: method names *Level() can take int pos or Proxy as arg.
*/
Index(std::string anIndexName = "Unnamed Pack",
std::string aGroupName = INDEX_DEFAULT_GROUP,
double defaultLocation = INDEX_DEFAULT_PACK_LOCATION);
~Index();
std::string getName();
std::string getGroupName();
std::string getDefaultGroupName();
double getLocation();
double getDefaultLocation();
void setDefaultLocation(double defLocation);
void moveToGroup(std::string groupName);
void locateBehind(std::string indexName);
void renameIndex(std::string newName);
virtual bool isSource(Proxy * aProxy);
int getCurrentPosition(); // 0 .. size-1
int getCurrentLevel(); // 1 .. size
Proxy * getCurrent();
void setCurrentPosition(int newPos);
int getScreenFirstPosition();
void setScreenFirstPosition(int iFirstPos);
virtual bool mayPlayLevel(int levelNumber);
Proxy * getProxy(int pos);
bool containsProxy(Proxy * aProxy);
bool hasNormLevelPath(std::string path);
virtual bool advanceLevel(LevelAdvanceMode advMode);
/*! Return number of levels */
virtual int size() const;
virtual void appendProxy(Proxy * newLevel, controlType varCtrl = force,
scoreUnitType varUnit = duration, std::string varTarget = "time",
std::map<std::string, std::string> varExtensions = nullExtensions);
virtual void clear();
void updateFromProxies();
// ---------- LevelPack legacy methods ---to be renamed ------- */
/*! Return the default SoundSet (see options::SoundSet for meaning) */
virtual const char* get_default_SoundSet() const;
/*! Returns true if it's a twoplayer levelpack, but has no
it-yinyang (needed to add it-yinyang to inventory if
oxyd-linkgame is played as single-player) */
virtual bool needs_twoplayers() const;
protected:
std::string indexName;
std::string indexGroup;
double indexLocation;
std::string defaultGroup;
double indexDefaultLocation;
int currentPosition; // 0,...
int screenFirstPosition; // LevelWidget ifirst
std::vector<Proxy *> proxies;
static std::map<std::string, std::string> nullExtensions;
private:
/**
* A map of index names to the indices themselves.
*/
static std::map<std::string, Index *> indices;
/**
* A map of index group names to vectors of indices. The vectors
* are sorted by the user sequence preference in the index group menu.
* Every index is listed in the group the user asigned it to.
*/
static std::map<std::string, std::vector<Index *> *> indexGroups;
/**
* Current active index. This index is selected in the Levelpack menu,
* shown and used in the submenus and stored in the user preferences.
* It's default is the "Tutorial" index.
*/
static Index * currentIndex;
static std::string currentGroup;
static void addIndexToGroup(Index *anIndex, std::vector<Index *> * aGroup);
static void removeIndexFromGroup(Index *anIndex, std::string groupName);
};
}} // namespace enigma::lev
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LEV_PERSISTENTINDEX_HH_INCLUDED
#define LEV_PERSISTENTINDEX_HH_INCLUDED
#include "lev/Index.hh"
#include <string>
#include <istream>
#include <xercesc/dom/DOMDocument.hpp>
#define INDEX_STD_FILENAME "index.xml"
namespace enigma { namespace lev {
struct Variation {
// Constructor
Variation(controlType ctrlValue = force, scoreUnitType unitValue = duration,
std::string targetValue = "time");
controlType ctrl;
scoreUnitType unit;
std::string target;
std::map<std::string, std::string> extensions;
bool operator == (const Variation& otherVar);
};
/**
*
*/
class PersistentIndex : public Index {
public:
static void registerPersistentIndices(bool onlySystemIndices);
static PersistentIndex * historyIndex;
static void addCurrentToHistory();
/**
* Convention: method names *Level() can take int pos or Proxy as arg.
*/
/**
*
* thePackPath " " for a new not yet defined path
*/
PersistentIndex(std::string thePackPath, bool systemOnly,
double defaultLocation = INDEX_DEFAULT_PACK_LOCATION,
std::string anIndexName = "",
std::string theIndexFilename = INDEX_STD_FILENAME,
std::string aGroupName = INDEX_DEFAULT_GROUP);
/**
* Legacy 0.92 constructor - called once to convert the index to XML.
* When the index has been stored as XML this constructor will not be
* called again.
*/
PersistentIndex(std::istream *legacyIndex, std::string thePackPath, bool isZip = false,
std::string anIndexName = "", std::string theIndexFilename = INDEX_STD_FILENAME);
~PersistentIndex();
void load(bool systemOnly, bool update = false);
void loadDoc();
std::string getPackPath();
bool setName(std::string newName);
std::string getOwner();
void setOwner(std::string newOwner);
int getRelease();
void setRelease(int newRelease);
int getRevision();
void setRevision(int newRevision);
double getCompatibility();
void setCompatibility(double newCompatibility);
bool isUserEditable();
bool isUpdatable();
bool isCross();
void markNewAsCross();
virtual void clear();
virtual void appendProxy(Proxy * newLevel, controlType varCtrl = force,
scoreUnitType varUnit = duration, std::string varTarget = "time",
std::map<std::string, std::string> varExtensions = nullExtensions);
void insertProxy(int pos, Proxy * newLevel, bool allowDuplicates = true,
controlType varCtrl = force, scoreUnitType varUnit = duration,
std::string varTarget = "time",
std::map<std::string, std::string> varExtensions = nullExtensions);
Variation getVariation(int pos);
void erase(int pos);
void exchange(int pos1, int pos2);
virtual bool isSource(Proxy * aProxy);
bool save(bool allowOverwrite = true);
protected:
std::string packPath; // "auto", "",...
std::string indexFilename;
std::string owner;
int release;
int revision;
double compatibility;
std::vector<Variation> variations;
bool isModified;
bool isUserOwned;
bool isEditable;
std::string indexUrl;
private:
static std::vector<PersistentIndex *> indexCandidates;
std::string absIndexPath;
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *infoElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *updateElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *levelsElem;
static void checkCandidate(PersistentIndex * candidate);
// legacy 0.92
void parsePar(const string& par, int& par_value, std::string& par_text);
};
void AddLevelPack (const char *init_file, const char *name);
void AddZippedLevelPack (const char *zipfile);
}} // namespace enigma::lev
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LEV_PROXY_HH_INCLUDED
#define LEV_PROXY_HH_INCLUDED
#include "enigma.hh"
#include <map>
#include <set>
#include <string>
#include <xercesc/dom/DOMDocument.hpp>
namespace enigma { namespace lev {
enum controlType {force, balance, key, other};
enum scoreUnitType {duration, number};
enum scoreTargetType {time, pushes, moves, callback};
enum levelStatusType {
STATUS_RELEASED,
STATUS_STABLE,
STATUS_TEST,
STATUS_EXPERIMENTAL,
STATUS_UNKNOWN
};
/**
* A standin for an addressable level file and its level metadata.
* Every level index and the commandline register their levels with the
* known level metadata. The unique proxy is henceforth used to access
* level data and to load the level. The level metadata are updated on
* level load and can be updated without a complete load of the level.
*
* The supplied level address is analysed and normalized to a unique
* schema. Urls, Enigma resource path addresses, absolute paths and
* Oxyd level addresses are supported.
*
* Mismatches of levels to requested levels are detected on level load.
* Old level revisions on the users Enigma home directory shadowing new
* revisions of a new Enigma installation will be handled, too.
*/
class Proxy {
public:
enum pathType { pt_url, pt_resource, pt_absolute, pt_oxyd};
static const XMLCh levelNS[]; // the XML namespace
static Proxy *loadedLevel(); // tmp ?
/**
* The registration of a level.
* @arg levelPath as stored in indices or entered on the commandline.
* valid formats are:
* welcome, ./firefox, stable/welcome:, ftp://..., #oxyd#17
* @arg indexPath a path identifier of the index in strict standard form:
* stable, "", #commandline, #history, #oxyd, http://...
* resource path level packs use the subdirectory name below
* levels or "", zipped packs the filename without suffix
* @arg levelId the version independent level id as used for scoring
* @arg levelTitle the english title of the level
* @arg levelAuthor the name of the author
* @arg levelScoreVersion the score version
* @arg levelRelease the level compatibility release version
* @arg levelHasEasymode support of an easy mode
*/
static Proxy *registerLevel(std::string levelPath, std::string indexPath,
std::string levelId, std::string levelTitle, std::string levelAuthor,
int levelScoreVersion, int levelRelease, bool levelHasEasymode,
GameType levelCompatibilty, levelStatusType status =STATUS_RELEASED,
int levelRevision = 0);
static Proxy *autoRegisterLevel(std::string indexPath, std::string filename);
static std::string search(std::string text);
static void countLevels();
static std::set<std::string> getLevelIds(bool withEasy);
static std::set<Proxy *> getProxies();
void loadLevel();
void loadMetadata(bool expectLevel);
Proxy * copy(std::string newBasePath, std::string newPackPath, bool backup = true);
/**
* Retrieve and translate a level string. The key may be "title",
* "subtitle" or any level specific string key. The priorities for
* translation are as follows: protected translation - gettext
* translation - public translation - protected english - key
* @arg key the key for the search string
* @return the translation of the string
*/
std::string getLocalizedString(const std::string &key);
std::string getId();
int getScoreVersion();
int getReleaseVersion();
int getRevisionNumber();
levelStatusType getLevelStatus();
std::string getAuthor();
std::string getTitle(); // english title
bool hasEasymode();
std::string getContact();
std::string getHomepage();
controlType getControl();
scoreUnitType getScoreUnit();
std::string getScoreTarget();
std::string getCredits(bool infoUsage);
std::string getDedication(bool infoUsage);
int getEasyScore();
int getDifficultScore();
GameType getEngineCompatibility();
double getEnigmaCompatibility();
/**
* the level address that can be used independent of a level pack
* as a crossreference. (stable/welcome, #oxyd#17, http://..., ~/test)
*/
std::string getNormLevelPath();
/**
* The normalized level path with all critical characters substituted
* by '~' to allow url's to be used and to make generated paths portable.
* Url protocols like "http://" are substituted by "http/". The returned
* path can be used as a local path element.
*/
std::string getLocalSubstitutionLevelPath();
/**
* the type of the level address
*/
Proxy::pathType getNormPathType();
std::string getAbsLevelPath();
void loadDependency(std::string depId);
void release();
private:
static Proxy *currentLevel;
static std::map<std::string, Proxy *> cache;
static std::vector<Proxy *> loadedLibs;
static std::vector<Proxy *> registeredLibs;
static void releaseLibs();
bool isLibraryFlag;
pathType normPathType;
std::string normLevelPath; // stable/welcome, #oxyd#17, http://..., ~/test
std::string absLevelPath;
std::string id; // level id - old filename or indexname
std::string title; // old name
std::string author;
int scoreVersion;
int releaseVersion;
int revisionNumber;
levelStatusType levelStatus;
bool hasEasymodeFlag;
scoreUnitType scoreUnit;
/**
* The compatibility that needs to be preset on level load.
* Usually level set the compatibility themselves on load. But heritage
* levels loaded from DAT-files do not. Thus we need to keep the type
* info in the Proxy as an exception.
*/
GameType engineCompatibility;
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *infoElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList *stringList;
Proxy(bool proxyIsLibrary, pathType thePathType, std::string theNormLevelPath,
std::string levelId, std::string levelTitle, std::string levelAuthor,
int levelScoreVersion, int levelRelease, bool levelHasEasymode,
GameType levelCompatibilty, levelStatusType status, int levelRevision = 0);
~Proxy();
void load(bool onlyMetadata, bool expectLevel);
void loadDoc();
void loadLuaCode();
void processDependencies();
void registerPreloadDependency(std::string depPath, std::string depId,
int depRelease, bool depPreload, std::string depUrl);
std::string getType();
bool updateId();
bool updateReleaseVersion();
int scoreText2Int(std::string text);
};
}} // namespace enigma::lev
#endif

View File

@@ -0,0 +1,698 @@
/*
* Copyright (C) 2006, 2007 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "lev/RatingManager.hh"
#include "main.hh"
#include "ecl_util.hh"
#include "errors.hh"
#include "LocalToXML.hh"
#include "Utf8ToXML.hh"
#include "XMLtoUtf8.hh"
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XercesVersion.hpp>
#if _XERCES_VERSION < 30000
#include <xercesc/framework/LocalFileFormatTarget.hpp>
#endif
using namespace std;
using namespace enigma;
XERCES_CPP_NAMESPACE_USE
namespace enigma { namespace lev {
std::time_t timeValue(const std::string &timeString) {
std::tm time;
time.tm_year = atoi(timeString.substr(0,4).c_str()) - 1900;
time.tm_mon = atoi(timeString.substr(5,2).c_str()) - 1;
time.tm_mday = atoi(timeString.substr(8,2).c_str());
time.tm_hour = atoi(timeString.substr(11,2).c_str());
time.tm_min = atoi(timeString.substr(14,2).c_str());
time.tm_sec = atoi(timeString.substr(17,2).c_str());
time.tm_isdst = false;
return std::mktime(&time);
}
std::time_t timeValue(const XMLCh * const string) {
std::string timeString = XMLtoUtf8(string).c_str();
return timeValue(timeString);
}
Rating::Rating()
{
intelligence = 0;
dexterity = 0;
patience = 0;
knowledge = 0;
speed = 0;
bestScoreEasy = -1; //DEFAULT_TIME;
bestScoreDifficult = -1; //DEFAULT_TIME;
parScoreEasy = -1;
parScoreDifficult = -1;
numSolvedEasy = 0;
numSolvedDifficult = 0;
pdmSolvedEasy = 0;
pdmSolvedDifficult = 0;
averageRating = -1;
}
RatingManager *RatingManager::theSingleton = 0;
RatingManager* RatingManager::instance() {
if (theSingleton == 0) {
theSingleton = new RatingManager();
}
return theSingleton;
}
RatingManager::RatingManager() : didAddRatings (false), didEditRatings (false) {
std::tm time;
time.tm_year = 2006 - 1900; // the past - all ratings are newer as this
time.tm_mon = 02; // is the date it was coded
time.tm_mday = 30;
time.tm_hour = 20;
time.tm_min = 10;
time.tm_sec = 00;
time.tm_isdst = false;
ratingVersion = std::mktime(&time);
if(app.state->getString("RatingsUpdateTime").empty()) {
// guarantee usable "RatingsUpdateTime"
char str[22];
std::strftime(str, 22, "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&ratingVersion));
std::string currentTimeString = str;
app.state->setProperty("RatingsUpdateTime", currentTimeString);
}
std::string sysRatingPath;
std::string userRatingPath;
if (app.systemFS->findFile(RATINGSFILENAME, sysRatingPath)) {
// preload cache with application distributed ratings
loadURI(sysRatingPath);
}
if (app.resourceFS->findFile(RATINGSFILENAME, userRatingPath) &&
userRatingPath != sysRatingPath) {
// update with user saved ratings - ignore cache changes as there
// is no reason to store the cache.
loadURI(userRatingPath);
}
if (app.prefs->getBool("RatingsAutoUpdate") == true)
update();
}
RatingManager::~RatingManager() {
}
RatingManager::loadResult RatingManager::loadURI(std::string path) {
RatingManager::loadResult status = checked;
try {
app.domParserErrorHandler->resetErrors();
app.domParserErrorHandler->reportToErr();
app.domParserSchemaResolver->resetResolver();
app.domParserSchemaResolver->addSchemaId("ratings.xsd","ratings.xsd");
DOMDocument *doc = app.domParser->parseURI(path.c_str());
if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) {
DOMElement *updateElem =
dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("update").x_str())->item(0));
std::time_t newVersion = timeValue(updateElem->getAttribute(Utf8ToXML("date").x_str()));
bool isUpdate = (std::difftime(newVersion, ratingVersion) > 0) ? true : false;
if (isUpdate) {
ratingVersion = newVersion;
ratingVersionString = XMLtoUtf8(updateElem->getAttribute(Utf8ToXML("date").x_str())).c_str();
urlFullUpdate = XMLtoUtf8(updateElem->getAttribute(Utf8ToXML("urlFull").x_str())).c_str();
urlIncrementalUpdate = XMLtoUtf8(updateElem->getAttribute(Utf8ToXML("urlIncremental").x_str())).c_str();
updateMinDelay = XMLString::parseInt(updateElem->getAttribute(Utf8ToXML("delay").x_str()));
}
DOMNodeList *levelList = doc->getElementsByTagName(
Utf8ToXML("level").x_str());
for (int i = 0, l = levelList-> getLength(); i < l; i++) {
DOMElement *levelElem = dynamic_cast<DOMElement *>(levelList->item(i));
const XMLCh * attr = levelElem->getAttribute(Utf8ToXML("id").x_str());
std::string id = XMLtoUtf8(attr).c_str();
attr = levelElem->getAttribute(Utf8ToXML("sv").x_str());
short sv = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
std::string cacheKey = id + "#" + ecl::strf("%d", sv);
std::map<std::string, Rating *>::iterator it = cache.find(cacheKey);
Rating * theRating;
bool isNewRating = false;
if (it != cache.end()) {
if (isUpdate)
// update the outdated entry
theRating = it->second;
else
// keep the newer entry
continue;
} else {
// add not existing entries in all cases
theRating = new Rating();
isNewRating = true;
}
status = updated;
attr = levelElem->getAttribute(Utf8ToXML("int").x_str());
theRating->intelligence = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("dex").x_str());
theRating->dexterity = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("pat").x_str());
theRating->patience = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("kno").x_str());
theRating->knowledge = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("spe").x_str());
theRating->speed = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("bse").x_str());
theRating->bestScoreEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1;
attr = levelElem->getAttribute(Utf8ToXML("bseh").x_str());
theRating->bestScoreEasyHolder = XMLtoUtf8(attr).c_str();
attr = levelElem->getAttribute(Utf8ToXML("bsd").x_str());
theRating->bestScoreDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1;
attr = levelElem->getAttribute(Utf8ToXML("bsdh").x_str());
theRating->bestScoreDifficultHolder = XMLtoUtf8(attr).c_str();
attr = levelElem->getAttribute(Utf8ToXML("pare").x_str());
theRating->parScoreEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1;
attr = levelElem->getAttribute(Utf8ToXML("pard").x_str());
theRating->parScoreDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1;
attr = levelElem->getAttribute(Utf8ToXML("solvne").x_str());
theRating->numSolvedEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("solvpe").x_str());
theRating->pdmSolvedEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("solvnd").x_str());
theRating->numSolvedDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("solvpd").x_str());
theRating->pdmSolvedDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0;
attr = levelElem->getAttribute(Utf8ToXML("avgur").x_str());
theRating->averageRating = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1;
if (isNewRating)
cache.insert(std::make_pair(cacheKey, theRating));
}
doc->release();
} else {
status = error;
}
}
catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
cerr << "Exception on load of preferences: "
<< message << "\n";
XMLString::release(&message);
return error;
}
catch (const DOMException& toCatch) {
char* message = XMLString::transcode(toCatch.msg);
cerr << "Exception on load of preferences: "
<< message << "\n";
XMLString::release(&message);
return error;
}
catch (...) {
cerr << "Unexpected Exception on load of preferences\n" ;
return error;
}
return status;
}
void RatingManager::update() {
std::time_t currentTime = std::time(NULL);
// check if an update is allowed
std::time_t currentWeirdTime = std::mktime(gmtime(&currentTime));
double updateDelay = std::difftime(currentWeirdTime,
timeValue(app.state->getString("RatingsUpdateTime")));
if (updateDelay/86400 < updateMinDelay) {
Log << "Rating update not yet allowed: " <<updateDelay<< " s\n";
return;
}
Log << "Rating update allowed\n";
// do update
bool didUpdate = false;
loadResult result;
std::string oldUrlIncrementalUpdate;
std::string oldUrlFullUpdate;
do {
Log << "Ratings update from '" << urlIncrementalUpdate << "'\n";
oldUrlIncrementalUpdate = urlIncrementalUpdate;
oldUrlFullUpdate = urlFullUpdate;
result = loadURI(urlIncrementalUpdate);
if (result == updated)
didUpdate = true;
} while (result != error &&
oldUrlIncrementalUpdate != urlIncrementalUpdate &&
oldUrlIncrementalUpdate != oldUrlFullUpdate);
if (result == error) {
// check if we are allowed for an full update
if (updateDelay/86400 > 3 * updateMinDelay) {
result = loadURI(urlFullUpdate);
}
}
if (result != error) {
// save current time as rating update time
char str[22];
std::strftime(str, 22, "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&currentTime));
std::string currentTimeString = str;
app.state->setProperty("RatingsUpdateTime", currentTimeString);
}
if (didUpdate)
didAddRatings = true; // we need to save the cache
else if (result == checked)
; // the update had no new ratings
}
// the following local vars should be ivars or arguments to doit - but
// as <algorithm> is anti OO it does not allow to transfer a context.
DOMDocument *saveDoc;
DOMElement *levelsElem;
void saveLevelRating(const std::map<std::string, Rating *>::value_type info) {
DOMElement *level = saveDoc->createElement(Utf8ToXML("level").x_str());
std::string id = info.first.substr(0, info.first.rfind('#'));
std::string sv = info.first.substr(info.first.rfind('#')+1);
level->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(id.c_str()).x_str());
level->setAttribute(Utf8ToXML("sv").x_str(), Utf8ToXML(sv.c_str()).x_str());
level->setAttribute(Utf8ToXML("int").x_str(), Utf8ToXML(ecl::strf("%d",info.second->intelligence).c_str()).x_str());
level->setAttribute(Utf8ToXML("dex").x_str(), Utf8ToXML(ecl::strf("%d",info.second->dexterity).c_str()).x_str());
level->setAttribute(Utf8ToXML("pat").x_str(), Utf8ToXML(ecl::strf("%d",info.second->patience).c_str()).x_str());
level->setAttribute(Utf8ToXML("kno").x_str(), Utf8ToXML(ecl::strf("%d",info.second->knowledge).c_str()).x_str());
level->setAttribute(Utf8ToXML("spe").x_str(), Utf8ToXML(ecl::strf("%d",info.second->speed).c_str()).x_str());
level->setAttribute(Utf8ToXML("bse").x_str(), Utf8ToXML(ecl::strf("%d",info.second->bestScoreEasy).c_str()).x_str());
level->setAttribute(Utf8ToXML("bsd").x_str(), Utf8ToXML(ecl::strf("%d",info.second->bestScoreDifficult).c_str()).x_str());
level->setAttribute(Utf8ToXML("bseh").x_str(), Utf8ToXML(info.second->bestScoreEasyHolder.c_str()).x_str());
level->setAttribute(Utf8ToXML("bsdh").x_str(), Utf8ToXML(info.second->bestScoreDifficultHolder.c_str()).x_str());
level->setAttribute(Utf8ToXML("pare").x_str(), Utf8ToXML(ecl::strf("%d",info.second->parScoreEasy).c_str()).x_str());
level->setAttribute(Utf8ToXML("pard").x_str(), Utf8ToXML(ecl::strf("%d",info.second->parScoreDifficult).c_str()).x_str());
level->setAttribute(Utf8ToXML("solvne").x_str(), Utf8ToXML(ecl::strf("%d",info.second->numSolvedEasy).c_str()).x_str());
level->setAttribute(Utf8ToXML("solvpe").x_str(), Utf8ToXML(ecl::strf("%d",info.second->pdmSolvedEasy).c_str()).x_str());
level->setAttribute(Utf8ToXML("solvnd").x_str(), Utf8ToXML(ecl::strf("%d",info.second->numSolvedDifficult).c_str()).x_str());
level->setAttribute(Utf8ToXML("solvpd").x_str(), Utf8ToXML(ecl::strf("%d",info.second->pdmSolvedDifficult).c_str()).x_str());
level->setAttribute(Utf8ToXML("avgur").x_str(), Utf8ToXML(ecl::strf("%d",info.second->averageRating).c_str()).x_str());
levelsElem->appendChild(level);
}
void RatingManager::save() {
if (!(didAddRatings || didEditRatings))
return;
if (didEditRatings) {
// set the rating version to the current time
char date[25];
std::time_t current = std::time(NULL);
strftime(date, 25, "%Y-%m-%dT%H:%M:%SZ",gmtime(&current));
ratingVersionString = date;
}
std::string ratTemplatePath;
if (!app.systemFS->findFile("schemas/ratings.xml" , ratTemplatePath)) {
cerr << "Ratings: no template found\n";
return;
}
try {
app.domParserErrorHandler->resetErrors();
app.domParserErrorHandler->reportToErr();
app.domParserSchemaResolver->resetResolver();
app.domParserSchemaResolver->addSchemaId("ratings.xsd","ratings.xsd");
saveDoc = app.domParser->parseURI(ratTemplatePath.c_str());
if (saveDoc != NULL && !app.domParserErrorHandler->getSawErrors()) {
DOMElement * updateElem = dynamic_cast<DOMElement *>(saveDoc->getElementsByTagName(
Utf8ToXML("update").x_str())->item(0));
updateElem->setAttribute(Utf8ToXML("date").x_str(), Utf8ToXML(ratingVersionString.c_str()).x_str());
updateElem->setAttribute(Utf8ToXML("urlFull").x_str(), Utf8ToXML(urlFullUpdate.c_str()).x_str());
updateElem->setAttribute(Utf8ToXML("urlIncremental").x_str(), Utf8ToXML(urlIncrementalUpdate.c_str()).x_str());
updateElem->setAttribute(Utf8ToXML("delay").x_str(), Utf8ToXML(ecl::strf("%d",updateMinDelay).c_str()).x_str());
levelsElem = dynamic_cast<DOMElement *>(saveDoc->getElementsByTagName(
Utf8ToXML("levels").x_str())->item(0));
std::for_each(cache.begin(), cache.end(), saveLevelRating);
std::string writePath = app.userPath + "/" + RATINGSFILENAME;
#if _XERCES_VERSION >= 30000
app.domSer->writeToURI(saveDoc, LocalToXML(writePath.c_str()).x_str());
#else
XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(writePath.c_str());
app.domSer->writeNode(myFormTarget, *saveDoc);
#endif
saveDoc->release();
}
}
catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
cerr << "Exception on load of preferences: "
<< message << "\n";
XMLString::release(&message);
return;
}
catch (const DOMException& toCatch) {
char* message = XMLString::transcode(toCatch.msg);
cerr << "Exception on load of preferences: "
<< message << "\n";
XMLString::release(&message);
return;
}
catch (...) {
cerr << "Unexpected Exception on load of preferences\n" ;
return;
}
}
void RatingManager::registerRating(std::string levelId, short levelScoreVersion,
short intelligence, short dexterity, short patience,
short knowledge, short speed, short bestScoreEasy,
std::string bestScoreEasyHolder, short bestScoreDifficult,
std::string bestScoreDifficultHolder) {
std::string cacheKey = levelId + "#" + ecl::strf("%d", levelScoreVersion);
std::map<std::string, Rating *>::iterator i = cache.find(cacheKey);
if (i != cache.end()) {
// Log << "Rating cache hit for " << levelId <<"\n";
return;
}
Rating *theRating = new Rating();
theRating->intelligence = intelligence;
theRating->dexterity = dexterity;
theRating->patience = patience;
theRating->knowledge = knowledge;
theRating->speed = speed;
theRating->bestScoreEasy = bestScoreEasy;
theRating->bestScoreEasyHolder = bestScoreEasyHolder;
theRating->bestScoreDifficult = bestScoreDifficult;
theRating->bestScoreDifficultHolder = bestScoreDifficultHolder;
cache.insert(std::make_pair(cacheKey, theRating));
didAddRatings = true;
return;
}
Rating * RatingManager::registerNewRating(Proxy * levelProxy) {
std::string cacheKey = levelProxy->getId() + "#" +
ecl::strf("%d", levelProxy->getScoreVersion());
std::map<std::string, Rating *>::iterator it = cache.find(cacheKey);
if (it != cache.end()) {
return it->second;
}
Rating *theRating = new Rating();
cache.insert(std::make_pair(cacheKey, theRating));
didAddRatings = true;
return theRating;
}
Rating * RatingManager::findRating(Proxy * levelProxy) {
std::string cacheKey = levelProxy->getId() + "#" +
ecl::strf("%d", levelProxy->getScoreVersion());
std::map<std::string, Rating *>::iterator it = cache.find(cacheKey);
if (it != cache.end()) {
Rating * theRating = it->second;
return theRating;
} else {
return NULL;
}
}
short RatingManager::getIntelligence(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->intelligence;
}
return 0;
}
void RatingManager::setIntelligence(Proxy *levelProxy, short intelligence) {
Rating * theRating = registerNewRating(levelProxy);
if (theRating != NULL) {
theRating->intelligence = intelligence;
didEditRatings = true;
}
}
short RatingManager::getDexterity(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->dexterity;
}
return 0;
}
void RatingManager::setDexterity(Proxy *levelProxy, short dexterity) {
Rating * theRating = registerNewRating(levelProxy);
if (theRating != NULL) {
theRating->dexterity = dexterity;
didEditRatings = true;
}
}
short RatingManager::getPatience(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->patience;
}
return 0;
}
void RatingManager::setPatience(Proxy *levelProxy, short patience) {
Rating * theRating = registerNewRating(levelProxy);
if (theRating != NULL) {
theRating->patience = patience;
didEditRatings = true;
}
}
short RatingManager::getKnowledge(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->knowledge;
}
return 0;
}
void RatingManager::setKnowledge(Proxy *levelProxy, short knowledge) {
Rating * theRating = registerNewRating(levelProxy);
if (theRating != NULL) {
theRating->knowledge = knowledge;
didEditRatings = true;
}
}
short RatingManager::getSpeed(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->speed;
}
return 0;
}
void RatingManager::setSpeed(Proxy *levelProxy, short speed) {
Rating * theRating = registerNewRating(levelProxy);
if (theRating != NULL) {
theRating->speed = speed;
didEditRatings = true;
}
}
short RatingManager::getDifficulty(Proxy *levelProxy) {
int difficulty = 7*getIntelligence(levelProxy) +
6*getDexterity(levelProxy) +
4*getPatience(levelProxy) +
3*getKnowledge(levelProxy) +
4*getSpeed(levelProxy) - 23;
return difficulty > 0 ? difficulty : 0;
}
short RatingManager::getBestScore(Proxy *levelProxy, int difficulty) {
if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode())
difficulty = DIFFICULTY_HARD;
switch (difficulty) {
case DIFFICULTY_EASY:
return getBestScoreEasy(levelProxy);
case DIFFICULTY_HARD:
return getBestScoreDifficult(levelProxy);
default:
ecl::Assert <XFrontend> (false, "RatingManager::getBestScore illegal difficulty");
}
return -1;
}
std::string RatingManager::cutHolders(std::string org, int factor) {
if (factor <= 0)
return org;
int others = 0;
if (org.rfind(" others") == org.length() - 7) {
// ratings string is already cut
std::string::size_type lastPlus = org.rfind('+');
std::istringstream othersString(org.substr(lastPlus + 1));
othersString >> std::dec >> others;
org = org.substr(0, lastPlus);
}
std::string::size_type cutPlus;
for (int i=0; i< factor; i++) {
cutPlus = org.rfind('+');
if (cutPlus == std::string::npos)
return "";
org = org.substr(0, cutPlus);
others++;
}
return ecl::strf("%s+%d others", org.c_str(), others);
}
std::string RatingManager::getBestScoreHolder(Proxy *levelProxy, int difficulty, int cut) {
if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode())
difficulty = DIFFICULTY_HARD;
switch (difficulty) {
case DIFFICULTY_EASY:
return getBestScoreEasyHolder(levelProxy, cut);
case DIFFICULTY_HARD:
return getBestScoreDifficultHolder(levelProxy, cut);
default:
ecl::Assert <XFrontend> (false, "RatingManager::getBestScoreHolder illegal difficulty");
}
return "";
}
short RatingManager::getBestScoreEasy(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->bestScoreEasy;
}
return -1;
}
std::string RatingManager::getBestScoreEasyHolder(Proxy *levelProxy, int cut) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return cutHolders(theRating->bestScoreEasyHolder, cut);
}
return "";
}
short RatingManager::getBestScoreDifficult(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->bestScoreDifficult;
}
return -1;
}
std::string RatingManager::getBestScoreDifficultHolder(Proxy *levelProxy, int cut) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return cutHolders(theRating->bestScoreDifficultHolder, cut);
}
return "";
}
short RatingManager::getParScore(Proxy *levelProxy, int difficulty) {
if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode())
difficulty = DIFFICULTY_HARD;
switch (difficulty) {
case DIFFICULTY_EASY:
return getParScoreEasy(levelProxy);
case DIFFICULTY_HARD:
return getParScoreDifficult(levelProxy);
default:
ecl::Assert <XFrontend> (false, "RatingManager::getParScore illegal difficulty");
}
return -1;
}
short RatingManager::getParScoreEasy(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->parScoreEasy;
}
return -1;
}
short RatingManager::getParScoreDifficult(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->parScoreDifficult;
}
return -1;
}
short RatingManager::getNumSolvedEasy(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->numSolvedEasy;
}
return 0;
}
short RatingManager::getNumSolvedDifficult(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->numSolvedDifficult;
}
return 0;
}
short RatingManager::getPdmSolvedEasy(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->pdmSolvedEasy;
}
return 0;
}
std::string RatingManager::getPcSolvedEasy(Proxy *levelProxy) {
std::string s = ecl::strf("%5.3d", getPdmSolvedEasy(levelProxy));
s.insert(3,1,'.');
return s;
}
short RatingManager::getPdmSolvedDifficult(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->pdmSolvedDifficult;
}
return 0;
}
std::string RatingManager::getPcSolvedDifficult(Proxy *levelProxy) {
std::string s = ecl::strf("%5.3d", getPdmSolvedDifficult(levelProxy));
s.insert(3,1,'.');
return s;
}
short RatingManager::getDAverageRating(Proxy *levelProxy) {
Rating * theRating = findRating(levelProxy);
if (theRating != NULL) {
return theRating->averageRating;
}
return -1;
}
std::string RatingManager::getAverageRating(Proxy *levelProxy) {
short rat = getDAverageRating(levelProxy);
std::string s;
if ( rat >= 0 ) {
s = ecl::strf("%3.2d", rat);
s.insert(2,1,'.');
} else {
s = "-";
}
return s;
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2006, 2007 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef RATINGMGR_HH_INCLUDED
#define RATINGMGR_HH_INCLUDED
#include "lev/Proxy.hh"
#include "enigma.hh"
#include <map>
#include <string>
#include <ctime>
namespace enigma { namespace lev {
/**
* The public rating info is currently just a data structure as the data
* have no behaviour. No public direct access to the data should occur.
*/
struct Rating
{
// Constructor
Rating();
// Constants
enum { DEFAULT_TIME = 99*60+59 };
// Variables.
short intelligence;
short dexterity;
short patience;
short knowledge;
short speed;
short bestScoreEasy; // Best time in seconds or Minimum moves to solve level(for easy mode)
short bestScoreDifficult; // Best time in seconds or Minimum moves to solve level(for normal mode)
std::string bestScoreEasyHolder; // player name(s) for 'best_time_easy'
std::string bestScoreDifficultHolder; // same for 'best_time_normal'
short parScoreEasy;
short parScoreDifficult;
int numSolvedEasy;
int numSolvedDifficult;
short pdmSolvedEasy; // per deca mille - 1/10000
short pdmSolvedDifficult;
short averageRating; // rating * 10
};
/**
* A singelton manager for public ratings and score statistics
* The singelton loads the ratings on initialization and caches them. An
* update methods allows to online update the ratings and a save method that
* should be called on game exit saves the cached ratings if they have been
* modified.
*
* The key to rating access is the Proxy. A rating is bound to a level id
* together with its scoring version.
* An up to date rating file is distributed with the application. These
* ratings are filled up by existing ratings and stored on the users enigma
* path. On an update which can be incremental or full the downloaded ratings
* replace their predecessors. All other ratings are kept.
*
* Only the rating administrator can and should edit ratings by copying
* the current offical rating file to his user enigma path, editing
* the ratings and leaving Enigma what saves the ratings with the current
* time stamp. The rating administrator has to ensure that he plays
* a clean Enigma version without any additional level packs on his
* resource path!
*
* Note: the time handling is a little bit weird due to limitations in the
* C++ standard libs. As no convertion for GMT to time_t exists and we
* would have to know the daylight saving offsets for a correct localtime
* handling we cheat with our time handling: We save correct GMT time values.
* But we read these values and convert them to local times without daylight
* saving correctures. This does not harm as we use these values only for
* comparisons and nothing more!
*/
class RatingManager {
public:
static RatingManager *instance();
~RatingManager();
/**
* Online updates the ratings from the update URLs. Online updates
* are only processed if the delay period after the last successful
* update is over. This feature reduced network access and server
* load.
* Unsuccessful online updates do not harm.
*/
void update();
/**
* Save the ratings if changes are pending.
*/
void save();
/**
* Registers an 0.92 index.txt rating in the cache. This rating will be
* used and stored in the new users ratings.xml only if no newer rating
* exists.
*/
void registerRating(std::string levelId, short levelScoreVersion,
short intelligence, short dexterity, short patience,
short knowledge, short speed, short bestScoreEasy,
std::string bestScoreEasyHolder, short bestScoreDifficult,
std::string bestScoreDifficultHolder);
short getIntelligence(Proxy *levelProxy);
void setIntelligence(Proxy *levelProxy, short intelligence);
short getDexterity(Proxy *levelProxy);
void setDexterity(Proxy *levelProxy, short dexterity);
short getPatience(Proxy *levelProxy);
void setPatience(Proxy *levelProxy, short patience);
short getKnowledge(Proxy *levelProxy);
void setKnowledge(Proxy *levelProxy, short knowledge);
short getSpeed(Proxy *levelProxy);
void setSpeed(Proxy *levelProxy, short speed);
short getDifficulty(Proxy *levelProxy);
short getBestScore(Proxy *levelProxy, int difficulty);
std::string getBestScoreHolder(Proxy *levelProxy, int difficulty, int cut = 0);
short getBestScoreEasy(Proxy *levelProxy);
std::string getBestScoreEasyHolder(Proxy *levelProxy, int cut = 0);
short getBestScoreDifficult(Proxy *levelProxy);
std::string getBestScoreDifficultHolder(Proxy *levelProxy, int cut = 0);
short getParScore(Proxy *levelProxy, int difficulty);
short getParScoreEasy(Proxy *levelProxy);
short getParScoreDifficult(Proxy *levelProxy);
short getNumSolvedEasy(Proxy *levelProxy);
short getNumSolvedDifficult(Proxy *levelProxy);
short getPdmSolvedEasy(Proxy *levelProxy);
std::string getPcSolvedEasy(Proxy *levelProxy);
short getPdmSolvedDifficult(Proxy *levelProxy);
std::string getPcSolvedDifficult(Proxy *levelProxy);
short getDAverageRating(Proxy *levelProxy);
std::string getAverageRating(Proxy *levelProxy);
protected:
RatingManager();
private:
enum loadResult { updated, checked, error};
static RatingManager *theSingleton;
std::map<std::string, Rating *> cache;
std::time_t ratingVersion;
std::string ratingVersionString;
std::string urlFullUpdate;
std::string urlIncrementalUpdate;
short updateMinDelay;
Rating * findRating(Proxy * levelProxy);
Rating * registerNewRating(Proxy * levelProxy);
std::string cutHolders(std::string org, int factor);
/**
* Loads the ratings from a given URI and updates the cached ratings.
* Not existing ratings are always added, existing ratings are updated
* only if the rating version is newer than the cached version.
* @arg path a local filepath or a URL
* @return ratings cache updated, no updates but file checked, or error
*/
loadResult loadURI(std::string path);
bool didAddRatings;
bool didEditRatings;
};
}} // namespace enigma::lev
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SCOREMGR_HH_INCLUDED
#define SCOREMGR_HH_INCLUDED
#include "PropertyManager.hh"
#include "lev/Proxy.hh"
#include "lev/RatingManager.hh"
#include "lev/Index.hh"
#include "main.hh"
#include <string>
#include <map>
#include <xercesc/dom/DOMElement.hpp>
namespace enigma { namespace lev {
// Constants
enum {
SCORE_MAX = 99*60+59,
SCORE_UNSOLVED = -1,
SCORE_SOLVED = -2
};
/**
* A singelton manager for user scores.
*
* There are 2 small paradigm shifts in 0.92 scores and 1.00 scores:<p>
* There is no longer a separate "finished" flag but levels that have
* been solved without a score value are marked as "SCORE_SOLVED".<p>
* Easy mode scores for levels that have no separate easy mode are stored
* as difficult mode scores. All requests for easy mode scores return the
* difficult mode scores in this case.
*/
class ScoreManager : public PropertyManager{
public:
static ScoreManager *instance();
~ScoreManager();
void markModified();
virtual bool save();
void shutdown();
/**
* Returns true if the level has been solved for the given difficulty
* in any score version.
* @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD, DIFFICULTY_ANY
*/
bool isSolved(lev::Proxy *levelProxy, int difficulty);
/**
* Returns true if the level has only been solved for the given difficulty
* in an outdated score version.
* @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD
*/
bool isOutdated(lev::Proxy *levelProxy, int difficulty);
/**
* Returns the best score that the user has reached for the given
* difficulty so far or -1 for not yet solved in the current score
* version.
* @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD
*/
int getBestUserScore(lev::Proxy *levelProxy, int difficulty);
/**
*
*/
void updateUserScore(lev::Proxy *levelProxy, int difficulty, int score,
double enigmaRelease = ENIGMACOMPATIBITLITY);
/**
* Returns true if the users best score is equal or better than the
* official best score for the given difficulty
* @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD
*/
bool bestScoreReached(lev::Proxy *levelProxy, int difficulty);
bool parScoreReached(lev::Proxy *levelProxy, int difficulty);
/**
* Resets the user score status to unsolved for the given diffculty.
* If the level has no easy mode the score for the difficult mode
* will be reset to unsolved.
*/
void markUnsolved(lev::Proxy *levelProxy, int difficulty);
/**
* Resets the score value and marks the level as solved for the given
* difficulty. If the level has no easy mode a request will mark the
* difficult mode score as solved. This action is restriced to
* wizards.
*/
void markSolved(lev::Proxy *levelProxy, int difficulty);
int countSolved(lev::Index *ind, int difficulty);
int countBestScore(lev::Index *ind, int difficulty);
int countParScore(lev::Index *ind, int difficulty);
double calcHCP(lev::Index *ind, int difficulty);
void setRating(lev::Proxy *levelProxy, int rating);
int getRating(lev::Proxy *levelProxy);
bool isRatingInherited(lev::Proxy *levelProxy);
protected:
ScoreManager();
private:
static ScoreManager *theSingleton;
static unsigned ctab[256];
static unsigned pol;
RatingManager *ratingMgr;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *levelsElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList * levelList;
std::map<std::string, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *> allLevelScores; // all scoreversions for each level
std::map<std::string, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *> curLevelScores; // most current scoreversion for each level
std::string userId;
bool hasValidUserId;
bool didUpgrade;
bool isModified;
void finishUserId(unsigned id3);
std::string sec(std::string target);
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * getLevel(lev::Proxy *levelProxy);
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * getCreateLevel(lev::Proxy *levelProxy);
unsigned idFromLegacyScore();
std::string upgradeSum();
bool upgradeLegacy();
};
}} // namespace enigma::lev
#endif

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "lev/VolatileIndex.hh"
#include "errors.hh"
#include "main.hh"
namespace enigma { namespace lev {
int VolatileIndex::levelCount = 0;
VolatileIndex::VolatileIndex(std::string anIndexName, std::string aGroupName,
const std::vector<string> levelpaths, double defaultLocation) :
Index(anIndexName, aGroupName, defaultLocation) {
for (unsigned i=0; i<levelpaths.size(); i++) {
levelCount++;
Proxy * aProxy = Proxy::registerLevel(levelpaths[i], "#commandline",
ecl::strf("_%d",levelCount), ecl::strf("Level %d", i), "unknown",
1, 0, false, GAMET_UNKNOWN, STATUS_UNKNOWN);
proxies.push_back(aProxy);
try {
aProxy->loadMetadata(true);
}
catch (XLevelLoading &err) {
Log << "Level load error on '" << levelpaths[i] << "\n";
}
}
}
VolatileIndex::~VolatileIndex() {}
void VolatileIndex::clear() {
proxies.clear();
currentPosition = 0;
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2006 Ronald Lamprecht
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LEV_VOLATILEINDEX_HH_INCLUDED
#define LEV_VOLATILEINDEX_HH_INCLUDED
#include "lev/Index.hh"
#include <string>
#include <vector>
namespace enigma { namespace lev {
/**
*
*/
class VolatileIndex : public Index {
public:
/**
* Convention: method names *Level() can take int pos or Proxy as arg.
*/
VolatileIndex(std::string anIndexName, std::string aGroupName,
const std::vector<string> levelpaths,
double defaultLocation = INDEX_DEFAULT_PACK_LOCATION);
~VolatileIndex();
virtual void clear();
private:
static int levelCount; // used for volatile level ids - necessary for lua levels
};
}} // namespace enigma::lev
#endif