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,103 @@
/*
* Copyright (C) 2005 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 "DOMErrorReporter.hh"
#include "main.hh"
#include "XMLtoLocal.hh"
#include <iostream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XercesDefs.hpp>
XERCES_CPP_NAMESPACE_USE
namespace enigma
{
DOMErrorReporter::DOMErrorReporter(std::ostream *aLogStream) :
sawErrors(false), severity(DOMError::DOM_SEVERITY_WARNING),
logStream(aLogStream), reportStream(aLogStream) {
}
DOMErrorReporter::~DOMErrorReporter() {
}
// DOMCountHandlers: Overrides of the DOM ErrorHandler interface
bool DOMErrorReporter::handleError(const DOMError& domError) {
sawErrors = true;
if (domError.getSeverity() == DOMError::DOM_SEVERITY_WARNING) {
if(reportStream) {*reportStream << "\nWarning ";}
}
else if (domError.getSeverity() == DOMError::DOM_SEVERITY_ERROR) {
if(reportStream) {*reportStream << "\nError ";}
if (severity == DOMError::DOM_SEVERITY_WARNING)
severity = DOMError::DOM_SEVERITY_ERROR;
}
else {
if(reportStream) {*reportStream << "\nFatal Error ";}
severity = DOMError::DOM_SEVERITY_FATAL_ERROR;
}
if(reportStream) {
const XMLCh * const fileURI = domError.getLocation()->getURI();
if( fileURI && (XMLString::stringLen(fileURI) > 0)) {
*reportStream << "at file "
<< XMLtoLocal(fileURI)
<< ", line " << domError.getLocation()->getLineNumber()
<< ", char " << domError.getLocation()->getColumnNumber();
}
*reportStream << "\n Message: " << XMLtoLocal(domError.getMessage())
<< std::endl;
}
// try to continue
return true;
}
bool DOMErrorReporter::getSawErrors() const {
return sawErrors;
}
DOMError::ErrorSeverity DOMErrorReporter::getSeverity() const {
return severity;
}
void DOMErrorReporter::resetErrors() {
sawErrors = false;
severity = DOMError::DOM_SEVERITY_WARNING;
}
void DOMErrorReporter::reportToLog() {
reportStream = logStream;
}
void DOMErrorReporter::reportToErr() {
reportStream = &std::cerr;
}
void DOMErrorReporter::reportToNull() {
reportStream = NULL;
}
void DOMErrorReporter::reportToOstream(std::ostream *anOstream) {
reportStream = anOstream;
}
} // namespace enigma

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2005 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 ENIGMA_DOMERRORREPORTER_HH
#define ENIGMA_DOMERRORREPORTER_HH
#include <ostream>
#include <xercesc/dom/DOMErrorHandler.hpp>
#include <xercesc/dom/DOMError.hpp>
namespace enigma
{
/**
* Error handler for DOM parser and serializer. It reports the error
* messages according to there severity as a log note or an alert panel
*/
class DOMErrorReporter : public XERCES_CPP_NAMESPACE_QUALIFIER DOMErrorHandler
{
public:
/**
* Main constructor.
* @param aLogStream the stream for error reports
*/
DOMErrorReporter(std::ostream *aLogStream);
~DOMErrorReporter();
/**
* Implements the DOM ErrorHandler interface. The error is reported
* and the maximum error typ is stored for polling evalutaion.
* @param domError the error description
* @return true to continue parse or serialization
*/
bool handleError(const XERCES_CPP_NAMESPACE_QUALIFIER DOMError& domError);
/**
* Returns true if error or warning occured since last reset
*/
bool getSawErrors() const;
/**
* Returns maximum severity of error since last reset
*/
XERCES_CPP_NAMESPACE_QUALIFIER DOMError::ErrorSeverity getSeverity() const;
/**
* Resets error flag and severity
*/
void resetErrors();
/**
* Switch on report output to log stream
*/
void reportToLog();
/**
* Switch on report output to err stream
*/
void reportToErr();
/**
* Switch off report output
*/
void reportToNull();
/**
* Switch on report output to given stream
*/
void reportToOstream(std::ostream *anOstream);
private :
// Unimplemented constructors and operators
DOMErrorReporter(const DOMErrorReporter&);
void operator=(const DOMErrorReporter&);
/**
* Flag for warnings or errors that occured since last reset
*/
bool sawErrors;
/**
* Most significant error type since last reset
*/
XERCES_CPP_NAMESPACE_QUALIFIER DOMError::ErrorSeverity severity;
/**
* Current output stream for error reports
*/
std::ostream *reportStream;
/**
* The configured log output stream
*/
std::ostream *logStream;
};
} // namespace enigma
#endif

View File

@@ -0,0 +1,127 @@
/*
* 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 "DOMSchemaResolver.hh"
#include "main.hh"
#include "file.hh"
#include "XMLtoLocal.hh"
#include "LocalToXML.hh"
#include <xercesc/dom/DOM.hpp>
#if _XERCES_VERSION >= 30000
#include <xercesc/framework/Wrapper4InputSource.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#else
#include <xercesc/framework/Wrapper4InputSource.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#endif
#include <iostream>
XERCES_CPP_NAMESPACE_USE
namespace enigma
{
DOMSchemaResolver::DOMSchemaResolver():resolveStatus(false) {
}
DOMSchemaResolver::~DOMSchemaResolver() {
}
void DOMSchemaResolver::resetResolver() {
substitutions.clear();
resolveStatus = false;
}
bool DOMSchemaResolver::didResolveSchema() {
return resolveStatus;
}
void DOMSchemaResolver::resetResolveStatus() {
resolveStatus = false;
}
void DOMSchemaResolver::addSchemaId(const std::string &schemaSystemId,
const std::string &schemaFilename) {
substitutions.insert(std::make_pair(schemaSystemId, schemaFilename));
}
#if _XERCES_VERSION >= 30000
DOMLSInput * DOMSchemaResolver::resolveResource (
const XMLCh* const resourceType, const XMLCh* const namespaceUri,
const XMLCh *const publicId, const XMLCh *const systemId,
const XMLCh *const baseURI) {
std::string schemaName = XMLtoLocal(systemId).c_str();
std::map<std::string, std::string>::iterator i = substitutions.find(schemaName);
if (i == substitutions.end()) {
Log << "DOMSchemaResolver: no schema substitution found for '"
<< schemaName << "'\n";
// let the parser try to resolve potential external entities
return NULL;
} else {
std::string schemaPath;
bool result = app.systemFS->findFile( std::string("schemas/") +
i->second , schemaPath);
if (result) {
DOMLSInput * inSrc = new Wrapper4InputSource(new LocalFileInputSource(
LocalToXML(&schemaPath).x_str()));
// W3C implementation:
// DOMLSInput * inSrc = app.domImplementationLS->createLSInput();
// inSrc->setSystemId(LocalToXML(&schemaPath).x_str()));
return inSrc;
} else {
Log << "DOMSchemaResolver: schema file '"
<< i->second << "' not found\n";
// let the parser try to resolve the schema
return NULL;
}
}
}
#else
DOMInputSource * DOMSchemaResolver::resolveEntity (
const XMLCh *const publicId, const XMLCh *const systemId,
const XMLCh *const baseURI) {
std::string schemaName = XMLtoLocal(systemId).c_str();
std::map<std::string, std::string>::iterator i = substitutions.find(schemaName);
if (i == substitutions.end()) {
Log << "DOMSchemaResolver: no schema substitution found for '"
<< schemaName << "'\n";
// let the parser try to resolve potential external entities
return NULL;
} else {
std::string schemaPath;
bool result = app.systemFS->findFile( std::string("schemas/") +
i->second , schemaPath);
if (result) {
DOMInputSource * inSrc = new Wrapper4InputSource(new LocalFileInputSource(
LocalToXML(&schemaPath).x_str()));
return inSrc;
} else {
Log << "DOMSchemaResolver: schema file '"
<< i->second << "' not found\n";
// let the parser try to resolve the schema
return NULL;
}
}
}
#endif
} // namespace enigma

View File

@@ -0,0 +1,91 @@
/*
* 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 ENIGMA_DOMSCHEMARESOLVER_HH
#define ENIGMA_DOMSCHEMARESOLVER_HH
#include <map>
#include <string>
#include <xercesc/util/XercesVersion.hpp>
#if _XERCES_VERSION >= 30000
#include <xercesc/dom/DOMLSParser.hpp>
#include <xercesc/dom/DOMLSResourceResolver.hpp>
#include <xercesc/dom/DOMLSInput.hpp>
#else
#include <xercesc/dom/DOMBuilder.hpp>
#include <xercesc/dom/DOMEntityResolver.hpp>
#include <xercesc/dom/DOMInputSource.hpp>
#endif
namespace enigma
{
/**
* An XML schema system id to local schema storage path resolver.
* The resolver can be configured with several pairs of system id and
* paths. This should allow usage of versioned schema system ids like
* http://enigma/1.0/levels.xsd . All entities without local substitution
* are resolved by the parser using the given system id. A flag allows to
* check if the XML file did adhere to a given schema.
*/
#if _XERCES_VERSION >= 30000
class DOMSchemaResolver : public XERCES_CPP_NAMESPACE_QUALIFIER DOMLSResourceResolver {
public:
XERCES_CPP_NAMESPACE_QUALIFIER DOMLSInput * resolveResource (
const XMLCh* const resourceType, const XMLCh* const namespaceUri,
const XMLCh *const publicId, const XMLCh *const systemId,
const XMLCh *const baseURI);
#else
class DOMSchemaResolver : public XERCES_CPP_NAMESPACE_QUALIFIER DOMEntityResolver {
public:
XERCES_CPP_NAMESPACE_QUALIFIER DOMInputSource * resolveEntity (
const XMLCh *const publicId, const XMLCh *const systemId,
const XMLCh *const baseURI);
#endif
DOMSchemaResolver();
~DOMSchemaResolver();
/**
* clear system id substitution paths and reset result flag.
*/
void resetResolver();
/**
* add a system id substituion path.
* @param schemaSystemId as used in XML files
* @param schemaFilename the local substitution filename of the schema.
* Just the filename without any path components.
*/
void addSchemaId(const std::string &schemaSystemId, const std::string &schemaFilename);
/**
* true if at least one local substitution occured since last reset.
*/
bool didResolveSchema();
/**
* just reset status without deletion of substitution info.
*/
void resetResolveStatus();
private:
std::map<std::string, std::string> substitutions;
bool resolveStatus;
};
} // namespace enigma
#endif

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2006 Daniel Heck
*
* 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 "Inventory.hh"
#include "items.hh"
#include "ecl_util.hh"
#include <algorithm>
using enigma::Inventory;
using world::Item;
typedef std::vector<Item*> ItemList;
/** The maximum number of items that may be stored in an inventory.
For compatibility with Oxyd this should be always 12. */
unsigned const Inventory::max_items = 12;
Inventory::Inventory() : m_items()
{}
Inventory::~Inventory()
{
clear();
}
size_t Inventory::size() const
{
return m_items.size();
}
void Inventory::clear()
{
ecl::delete_sequence(m_items.begin(), m_items.end());
m_items.clear();
}
Item * Inventory::get_item (size_t idx) const
{
return idx >= size() ? 0 : m_items[idx];
}
Item * Inventory::yield_item (size_t idx)
{
if (idx < size()) {
Item *it = m_items[idx];
m_items.erase(m_items.begin()+ idx);
return it;
}
return 0;
}
Item * Inventory::yield_first()
{
return yield_item(0);
}
Item * Inventory::remove_item(Item *wanted)
{
ItemList::iterator e = m_items.end();
for (ItemList::iterator i = m_items.begin(); i != e; ++i) {
if (*i == wanted) {
m_items.erase(i);
return wanted;
}
}
return 0;
}
bool Inventory::is_full() const
{
ItemHolder *holder = dynamic_cast<ItemHolder*>(get_item(0));
if (holder && !holder->is_full())
return false;
return m_items.size() >= max_items;
}
bool Inventory::is_empty() const {
return m_items.size() == 0;
}
void Inventory::add_item(Item *i)
{
ItemHolder *firstHolder = dynamic_cast<ItemHolder*>(get_item(0));
ItemHolder *addHolder = dynamic_cast<ItemHolder*>(i);
if (firstHolder && !firstHolder->is_full() &&
(addHolder == NULL || !addHolder->is_empty())) {
// first item is a bag and not full and add item is not an empty bag
firstHolder->add_item (i);
}
else {
m_items.insert (m_items.begin(), i);
}
}
void Inventory::takeItemsFrom(ItemHolder *ih) {
ItemHolder *holder = dynamic_cast<ItemHolder*>(get_item(0));
if (holder && !holder->is_full()) {
// first item is a bag and not full - do not refill items form one
// itemholder into another
return;
}
else {
while (m_items.size() < max_items && !ih->is_empty()) {
Item * it = ih->yield_first();
m_items.insert (m_items.begin(), it);
}
return;
}
}
void Inventory::rotate_left ()
{
if (!m_items.empty())
std::rotate(m_items.begin(), m_items.begin()+1, m_items.end());
}
void Inventory::rotate_right ()
{
if (!m_items.empty())
std::rotate(m_items.begin(), m_items.end()-1, m_items.end());
}
bool Inventory::willAddItem(Item *it) {
ItemHolder *holder = dynamic_cast<ItemHolder*>(it);
if (is_full()) {
return false;
} else if (holder != NULL && holder->is_empty() &&
(m_items.size() >= max_items || dynamic_cast<ItemHolder*>(get_item(0)) != NULL)) {
// should add a bag that is empty, but first item in Inventory is itself
// a bag or Inventory is full -- avoid recursive bags
return false;
}
return true; // we have space and item is not critical
}
int Inventory::find(const std::string& kind, size_t start_idx) const
{
size_t size_ = size();
for (size_t i = start_idx; i<size_; ++i) {
if (get_item(i)->is_kind(kind))
return static_cast<int> (i);
}
return -1;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2006 Daniel Heck
*
* 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 INVENTORY_HH_INCLUDED
#define INVENTORY_HH_INCLUDED
#include "ItemHolder.hh"
#include <string>
#include <vector>
#include <stdlib.h>
namespace enigma
{
using world::Item; // TODO: remove this after moving Item into enigma namespace
class Inventory : public ItemHolder {
public:
Inventory();
~Inventory();
// ---------- ItemHolder interface ----------
bool is_full() const;
virtual bool is_empty() const;
void add_item (Item *i);
virtual void takeItemsFrom(ItemHolder *ih);
virtual Item *yield_first();
// ---------- Methods ----------
//! The number of items currently in the inventory
size_t size() const;
void clear();
void rotate_left();
void rotate_right();
Item *get_item (size_t idx) const;
Item *yield_item (size_t idx);
bool willAddItem(Item *it);
int find(const std::string& kind, size_t start_idx = 0) const;
private:
// Private methods.
Item *remove_item(Item *it);
// Private variables.
static const unsigned max_items;
std::vector<Item*> m_items;
};
}
#endif

View File

@@ -0,0 +1,29 @@
#ifndef ITEMHOLDER_HH_INCLUDED
#define ITEMHOLDER_HH_INCLUDED
#include "items.hh"
namespace enigma
{
/**
* A base class for all entities that can hold multiple items
* during the game. Currently this is only the inventory that is
* displayed at the bottom of the screen (there is one inventory
* for every player) and the it-bag item.
*/
class ItemHolder {
public:
virtual ~ItemHolder() {}
//! Return true if not further object can be picked up
virtual bool is_full() const = 0;
virtual bool is_empty() const = 0;
//! Add another item
virtual void add_item (world::Item *it) = 0;
virtual world::Item *yield_first() = 0;
virtual void takeItemsFrom(ItemHolder *ih) {return;}
};
}
#endif

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005 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 "LocalToXML.hh"
XERCES_CPP_NAMESPACE_USE
namespace enigma
{
LocalToXML::LocalToXML(const char * const toTranscode) {
// Use XML for transcoding -- the returned string is owned by us
// but managed by XMLString!
xmlString = XMLString::transcode(toTranscode);
}
LocalToXML::LocalToXML(const std::string * const toTranscode) {
// Use XML for transcoding -- the returned string is owned by us
// but managed by XMLString!
xmlString = XMLString::transcode(toTranscode->c_str());
}
LocalToXML::~LocalToXML() {
XMLString::release(&xmlString);
}
const XMLCh * LocalToXML::x_str() const {
return xmlString;
}
} //namespace enigma

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2005 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 ENIGMA_LOCALTOXML_HH
#define ENIGMA_LOCALTOXML_HH
#include <iostream>
#include <xercesc/util/XMLString.hpp>
namespace enigma
{
/**
* Transcoding utility for local code page strings to XMLCh strings.
* Provides a simple interface for the memory management paradigm shift
* even though not terribly efficient. Make sure all LocalToXML objects
* exist only temporarily, f.e.
* <code>DOMNode::setNodeValue(LocalToXML(char *toTranscode).x_str())</code> or
* <code>XMLString::replicate(LocalToXML(char *toTranscode).x_str())</code>
* Xerces should be initialized before using this class and all objects
* should be deleted before terminating.
*/
class LocalToXML {
public :
/**
* Makes a transcoding to XML.
*
* @param toTranscode local code page string
*/
LocalToXML(const char * const toTranscode);
/**
* Makes a transcoding to XML.
*
* @param toTranscode local code page string
*/
LocalToXML(const std::string * const toTranscode);
~LocalToXML();
/**
* Returns the string coded in the local page. It remains owner of
* the string.
*/
const XMLCh * x_str() const;
private :
/**
* A XML copy. We are the owner.
*/
XMLCh * xmlString;
void init(const char * const toTranscode);
};
} //namespace enigma
#endif

View File

@@ -0,0 +1,197 @@
/*
* 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 "PreferenceManager.hh"
#include "main.hh"
#include "DOMErrorReporter.hh"
#include "DOMSchemaResolver.hh"
#include "LocalToXML.hh"
#include "nls.hh"
#include "Utf8ToXML.hh"
#include "utilXML.hh"
#include "options.hh"
#include "XMLtoLocal.hh"
#include "XMLtoUtf8.hh"
#include "ecl_system.hh"
#include "gui/ErrorMenu.hh"
#include <iostream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLDouble.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/PlatformUtils.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 {
PreferenceManager *PreferenceManager::theSingleton = 0;
PreferenceManager* PreferenceManager::instance() {
if (theSingleton == 0) {
theSingleton = new PreferenceManager();
}
return theSingleton;
}
PreferenceManager::PreferenceManager() {
std::string prefTemplatePath;
bool haveXMLProperties = (ecl::FileExists(app.prefPath)) ? true : false;
if (!app.systemFS->findFile( std::string("schemas/") + PREFFILENAME , prefTemplatePath)) {
cerr << "Preferences: no template found\n";
exit(-1);
}
try {
app.domParserErrorHandler->resetErrors();
app.domParserErrorHandler->reportToErr();
app.domParserSchemaResolver->resetResolver();
app.domParserSchemaResolver->addSchemaId("preferences.xsd","preferences.xsd");
if (haveXMLProperties) {
// update existing XML prefs from possibly newer template:
// use user prefs and copy new properties from template
doc = app.domParser->parseURI(app.prefPath.c_str());
propertiesElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("properties").x_str())->item(0));
// The following algorithm is not optimized - O(n^2)!
DOMDocument * prefTemplate = app.domParser->parseURI(prefTemplatePath.c_str());
DOMNodeList * tmplPropList = prefTemplate->getElementsByTagName(
Utf8ToXML("property").x_str());
for (int i = 0, l = tmplPropList-> getLength(); i < l; i++) {
DOMElement *tmplProperty = dynamic_cast<DOMElement *>(tmplPropList->item(i));
const XMLCh * key = tmplProperty->getAttribute(Utf8ToXML("key").x_str());
DOMElement * lastUserProperty;
if ((key[0] != chUnderscore) && !hasProperty(key, &lastUserProperty)) {
// a new property in the template
Log << "Preferences: add new Property \"" << XMLtoLocal(key) << "\"\n";
// make a copy
DOMNode * newProperty = doc->importNode(tmplProperty, false);
if (newProperty == NULL) {
Log << "Preferences: copy new Property failed!\n";
} else {
// insert it at the end of the existing user properties
propertiesElem->appendChild(dynamic_cast<DOMElement *>(newProperty));
}
}
}
prefTemplate->release();
} else {
// update from LUA options to XML preferences:
// use the template, copy LUA option values and save it later as prefs
doc = app.domParser->parseURI(prefTemplatePath.c_str());
propertiesElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("properties").x_str())->item(0));
DOMNodeList * propList = propertiesElem->getElementsByTagName(Utf8ToXML("property").x_str());
for (int i = 0, l = propList-> getLength(); i < l; i++) {
DOMElement * property = dynamic_cast<DOMElement *>(propList->item(i));
const XMLCh * key = property->getAttribute(Utf8ToXML("key").x_str());
std::string optionValue;
if (options::HasOption(XMLtoLocal(key).c_str(), optionValue)) {
Log << "Preferences: copy LUA option \"" << XMLtoLocal(key) << "\"\n";
property->setAttribute(Utf8ToXML("value").x_str(),
LocalToXML(&optionValue).x_str());
} else {
Log << "Preferences: no LUA option \"" << XMLtoLocal(key) << "\"\n";
}
}
}
}
catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
cerr << "Exception on load of preferences: "
<< message << "\n";
XMLString::release(&message);
exit (-1);
}
catch (const DOMException& toCatch) {
char* message = XMLString::transcode(toCatch.msg);
cerr << "Exception on load of preferences: "
<< message << "\n";
XMLString::release(&message);
exit (-1);
}
catch (...) {
cerr << "Unexpected Exception on load of preferences\n" ;
exit (-1);
}
}
PreferenceManager::~PreferenceManager() {
if (doc != NULL)
shutdown();
}
bool PreferenceManager::save() {
bool result = true;
std::string errMessage;
if (doc == NULL)
return true;
stripIgnorableWhitespace(doc->getDocumentElement());
try {
#if _XERCES_VERSION >= 30000
result = app.domSer->writeToURI(doc, LocalToXML(& app.prefPath).x_str());
#else
XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(app.prefPath.c_str());
result = app.domSer->writeNode(myFormTarget, *doc);
delete myFormTarget; // flush
#endif
} catch (const XMLException& toCatch) {
errMessage = std::string("Exception on save of preferences: \n") +
XMLtoUtf8(toCatch.getMessage()).c_str() + "\n";
result = false;
} catch (const DOMException& toCatch) {
errMessage = std::string("Exception on save of preferences: \n") +
XMLtoUtf8(toCatch.getMessage()).c_str() + "\n";
result = false;
} catch (...) {
errMessage = "Unexpected exception on save of preferences\n" ;
result = false;
}
if (!result) {
cerr << errMessage;
gui::ErrorMenu m(errMessage, N_("Continue"));
m.manage();
} else
Log << "Preferences save o.k.\n";
return result;
}
void PreferenceManager::shutdown() {
save();
if (doc != NULL)
doc->release();
doc = NULL;
}
} // namespace enigma

View File

@@ -0,0 +1,55 @@
/*
* 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 PREFMGR_HH_INCLUDED
#define PREFMGR_HH_INCLUDED
#include "PropertyManager.hh"
namespace enigma
{
/**
* A singelton manager for preferences stored in XML format.
* <p>The singleton can be accessed via standard static instance() method or
* the application public ivar <code>app.prefs</code>.</p>
* <p> During initialization old LUA stored options are convereted. New
* preferences with given default values are introduced to the user via the
* template in the systen data directory (data/enigmarc.xml).</p>
* <p>The storage location of the users preferences is determined by the
* Application object.</p>
* <p>The set and get methods for preferences take utf8 encoded preference
* names and operate with utf8 encoded string values. Set methods create new
* preference property elements if necessary. Get methods do not modify
* reference values and return C++ default values if the named preference
* does not exist.</p>
*/
class PreferenceManager : public PropertyManager {
public:
static PreferenceManager *instance();
~PreferenceManager();
virtual bool save();
void shutdown();
protected:
PreferenceManager();
private:
static PreferenceManager *theSingleton;
};
} // namespace enigma
#endif

View File

@@ -0,0 +1,186 @@
/*
* 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 "PropertyManager.hh"
#include "errors.hh"
#include "main.hh"
#include "DOMErrorReporter.hh"
#include "DOMSchemaResolver.hh"
#include "LocalToXML.hh"
#include "Utf8ToXML.hh"
#include "options.hh"
#include "XMLtoLocal.hh"
#include "XMLtoUtf8.hh"
#include "ecl_system.hh"
#include <cstdio>
#include <iostream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLDouble.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/PlatformUtils.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 {
PropertyManager::PropertyManager() : doc (NULL), propertiesElem (NULL) {
}
PropertyManager::~PropertyManager() {
}
void PropertyManager::setProperty(const char *prefName, const std::string &value) {
DOMElement * property = getPropertyElement(prefName);
property->setAttribute(Utf8ToXML("value").x_str(),
Utf8ToXML(&value).x_str());
}
void PropertyManager::setProperty(const char *prefName, const char *value) {
setProperty(prefName, std::string(value));
}
void PropertyManager::getProperty(const char *prefName, std::string &value) {
DOMElement * property;
bool propFound = hasProperty(prefName, &property);
if (propFound) {
value = XMLtoUtf8(property->getAttribute(Utf8ToXML("value").x_str())).c_str();
}
}
std::string PropertyManager::getString(const char *prefName) {
std::string value;
getProperty(prefName, value);
return value;
}
void PropertyManager::setProperty(const char *prefName, const double &value) {
char printedValue[20];
sprintf(printedValue, "%.7g", value);
DOMElement * property = getPropertyElement(prefName);
property->setAttribute(Utf8ToXML("value").x_str(),
Utf8ToXML(printedValue).x_str());
}
void PropertyManager::getProperty(const char *prefName, double &value) {
DOMElement * property;
bool propFound = hasProperty(prefName, &property);
if (propFound) {
XMLDouble * result = new XMLDouble(property->getAttribute(Utf8ToXML("value").x_str()));
value = result->getValue();
delete result;
}
}
double PropertyManager::getDouble(const char *prefName) {
double value = 0;
getProperty (prefName, value);
return value;
}
void PropertyManager::setProperty(const char *prefName, const int &value) {
char printedValue[20];
sprintf(printedValue, "%d", value);
DOMElement * property = getPropertyElement(prefName);
property->setAttribute(Utf8ToXML("value").x_str(),
Utf8ToXML(printedValue).x_str());
}
void PropertyManager::getProperty(const char *prefName, int &value) {
DOMElement * property;
bool propFound = hasProperty(prefName, &property);
if (propFound) {
value = XMLString::parseInt(property->getAttribute(Utf8ToXML("value").x_str()));
}
}
int PropertyManager::getInt(const char *prefName) {
int value = 0;
getProperty (prefName, value);
return value;
}
void PropertyManager::setProperty(const char *prefName, const bool &value) {
int i = (value ? 1: 0);
setProperty(prefName, i);
}
void PropertyManager::getProperty(const char *prefName, bool &value) {
int result = 0;
getProperty(prefName, result);
value = ((result == 0) ? false: true);
}
bool PropertyManager::getBool(const char *prefName) {
bool value = false;
getProperty (prefName, value);
return value;
}
DOMElement * PropertyManager::getPropertyElement(const char *prefName) {
ASSERT(propertiesElem != NULL, XFrontend, "");
ASSERT(doc != NULL, XFrontend, "");
XMLCh * key = XMLString::replicate(Utf8ToXML(prefName).x_str());
DOMElement * property;
bool propFound = hasProperty(prefName, &property);
if (!propFound) {
DOMElement * newProperty = doc->createElement (Utf8ToXML("property").x_str());
newProperty->setAttribute(Utf8ToXML("key").x_str(), key);
// insert it at the end of the existing properties
propertiesElem->appendChild(newProperty);
property = newProperty;
}
XMLString::release(&key);
return property;
}
bool PropertyManager::hasProperty(const char *prefName, DOMElement ** element) {
return hasProperty(Utf8ToXML(prefName).x_str(), element);
}
bool PropertyManager::hasProperty(const XMLCh * key, DOMElement ** element) {
ASSERT(propertiesElem != NULL, XFrontend, "");
bool propFound = false;
DOMElement * property;
// Xerces 3.0 has no full XPath support - otherwise the following simple
// statement would suffice and not be aborted with NOT_SUPPORTED_ERR.
// doc->evaluate(Utf8ToXML("//property[@key='...']").x_str(), doc, NULL, 0, NULL);
DOMNodeList * propList = propertiesElem->getElementsByTagName(Utf8ToXML("property").x_str());
for (int i = 0, l = propList-> getLength(); i < l && !propFound; i++) {
property = dynamic_cast<DOMElement *>(propList->item(i));
if (XMLString::equals(key,
property->getAttribute(Utf8ToXML("key").x_str()))) {
propFound = true;
}
}
* element = property;
return propFound;
}
} // namespace enigma

View File

@@ -0,0 +1,81 @@
/*
* 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 PROPERTYMGR_HH_INCLUDED
#define PROPERTYMGR_HH_INCLUDED
#include <string>
#include <xercesc/dom/DOMDocument.hpp>
#include <xercesc/dom/DOMElement.hpp>
namespace enigma
{
/**
* An abstact Superclass for key/value property access.
*/
class PropertyManager {
public:
~PropertyManager();
virtual bool save() = 0; // mark the class as abstract
void setProperty(const char *prefName, const std::string &value);
void setProperty(const char *prefName, const char *value);
void getProperty(const char *prefName, std::string &value);
std::string getString(const char *prefName);
void setProperty(const char *prefName, const double &value);
void getProperty(const char *prefName, double &value);
double getDouble(const char *prefName);
void setProperty(const char *prefName, const int &value);
void getProperty(const char *prefName, int &value);
int getInt(const char *prefName);
void setProperty(const char *prefName, const bool &value);
void getProperty(const char *prefName, bool &value);
bool getBool(const char *prefName);
protected:
PropertyManager();
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *propertiesElem;
/**
* force a return of a property element with the given name as key.
* @param prefName the name of the searched or new property
* @return the property element with the given key name.
*/
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * getPropertyElement(const char *prefName);
/**
* checks if a property exists.
* @param prefName the preference name.
* @param element the searched property element or the last property
* element found in the preference list (for append usage).
* @return validity of element.
*/
bool hasProperty(const char *prefName, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement ** element);
/**
* checks if a property exists.
* @param key the preference name.
* @param element the searched property element or the last property
* element found in the preference list (for append usage).
* @return validity of element.
*/
bool hasProperty(const XMLCh * key, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement ** element);
};
} // namespace enigma
#endif

View File

@@ -0,0 +1,408 @@
/*
* 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 "StateManager.hh"
#include "errors.hh"
#include "main.hh"
#include "DOMErrorReporter.hh"
#include "DOMSchemaResolver.hh"
#include "LocalToXML.hh"
#include "nls.hh"
#include "options.hh"
#include "Utf8ToXML.hh"
#include "utilXML.hh"
#include "XMLtoLocal.hh"
#include "XMLtoUtf8.hh"
#include "ecl_system.hh"
#include "gui/ErrorMenu.hh"
#include <cstdio>
#include <iostream>
#include <sstream>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLDouble.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/PlatformUtils.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 {
StateManager *StateManager::theSingleton = 0;
StateManager* StateManager::instance() {
if (theSingleton == 0) {
theSingleton = new StateManager();
}
return theSingleton;
}
StateManager::StateManager() {
std::string statePath;
std::string errMessage;
if (!app.resourceFS->findFile( "state.xml" , statePath)) {
if (!app.systemFS->findFile( "schemas/state.xml" , statePath)) {
throw XFrontend("Cannot load application state template!");
}
}
try {
std::ostringstream errStream;
app.domParserErrorHandler->resetErrors();
app.domParserErrorHandler->reportToOstream(&errStream);
app.domParserSchemaResolver->resetResolver();
app.domParserSchemaResolver->addSchemaId("state.xsd","state.xsd");
doc = app.domParser->parseURI(statePath.c_str());
if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) {
propertiesElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("properties").x_str())->item(0));
groupsElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("groups").x_str())->item(0));
groupList = groupsElem->getElementsByTagName(
Utf8ToXML("group").x_str());
indicesElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("indices").x_str())->item(0));
indexList = indicesElem->getElementsByTagName(
Utf8ToXML("index").x_str());
levelsElem = dynamic_cast<DOMElement *>(doc->getElementsByTagName(
Utf8ToXML("levels").x_str())->item(0));
}
if(app.domParserErrorHandler->getSawErrors()) {
errMessage = errStream.str();
}
app.domParserErrorHandler->reportToNull(); // do not report to errStream any more
}
catch (...) {
errMessage = "Unexpected XML Exception on load of state\n";
}
if (!errMessage.empty()) {
throw XFrontend("Cannot load application state file: " + statePath +
"\nError: " + errMessage);
}
}
StateManager::~StateManager() {
if (doc != NULL)
shutdown();
}
bool StateManager::save() {
bool result = true;
std::string errMessage;
if (doc == NULL)
return true;
int count = getInt("Count");
setProperty("Count", ++count);
stripIgnorableWhitespace(doc->getDocumentElement());
std::string path = app.userPath + "/state.xml";
std::string pathBackup = app.userPath + "/backup/state.xml";
// backup state every 10th save
if (count%10 == 0) {
std::remove((pathBackup + "~2").c_str());
std::remove((path + "~2").c_str()); // 1.00 bakups
if (ecl::FileExists(path + "~1")) {
if (std::difftime(ecl::FileModTime(path + "~1"),
ecl::FileModTime(pathBackup + "~1")) > 0) {
// backup 1 from 1.00 is newer than backup 1 on backup path
if (Copyfile(path + "~1", pathBackup + "~2"))
std::remove((path + "~1").c_str()); // 1.00 bakup
} else {
// just in case off previous copy failure
std::rename((pathBackup + "~1").c_str(), (pathBackup + "~2").c_str());
std::remove((path + "~1").c_str()); // 1.00 bakup
}
} else {
std::rename((pathBackup + "~1").c_str(), (pathBackup + "~2").c_str());
}
Copyfile(path, pathBackup + "~1");
}
try {
#if _XERCES_VERSION >= 30000
result = app.domSer->writeToURI(doc, LocalToXML(& path).x_str());
#else
XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(path.c_str());
result = app.domSer->writeNode(myFormTarget, *doc);
delete myFormTarget; // flush
#endif
} catch (const XMLException& toCatch) {
errMessage = std::string("Exception on save of state: \n") +
XMLtoUtf8(toCatch.getMessage()).c_str() + "\n";
result = false;
} catch (const DOMException& toCatch) {
errMessage = std::string("Exception on save of state: \n") +
XMLtoUtf8(toCatch.getMessage()).c_str() + "\n";
result = false;
} catch (...) {
errMessage = "Unexpected exception on save of state\n" ;
result = false;
}
if (!result) {
if (count%10 == 0) {
// restore backup in case of error
if (Copyfile(pathBackup + "~1", path)) {
std::remove((pathBackup + "~1").c_str());
std::rename((pathBackup + "~2").c_str(), (pathBackup + "~1").c_str());
}
}
cerr << XMLtoLocal(Utf8ToXML(errMessage.c_str()).x_str()).c_str();
gui::ErrorMenu m(errMessage, N_("Continue"));
m.manage();
} else
Log << "State save o.k.\n";
return result;
}
void StateManager::shutdown() {
save();
if (doc != NULL)
doc->release();
doc = NULL;
}
void StateManager::getGroupNames(std::vector<std::string> *names) {
for (int i = 0, l = groupList-> getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
names->push_back(XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str());
}
}
std::string StateManager::getGroupSelectedIndex(std::string groupName) {
for (int i = 0, l = groupList-> getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str())
return XMLtoUtf8(group->getAttribute(Utf8ToXML("curindex").x_str())).c_str();
}
return "";
}
std::string StateManager::getGroupSelectedColumn(std::string groupName) {
for (int i = 0, l = groupList-> getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str())
return XMLtoUtf8(group->getAttribute(Utf8ToXML("curcolumn").x_str())).c_str();
}
return 0;
}
void StateManager::setGroupSelectedIndex(std::string groupName, std::string indexName) {
for (int i = 0, l = groupList-> getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
return;
}
}
}
void StateManager::setGroupSelectedColumn(std::string groupName, std::string column) {
for (int i = 0, l = groupList-> getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
group->setAttribute(Utf8ToXML("curcolumn").x_str(), Utf8ToXML(column).x_str());
return;
}
}
}
void StateManager::addGroup(std::string groupName, std::string indexName, int column) {
// check if group exists - update attributes only
for (int i = 0, l = groupList->getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
group->setAttribute(Utf8ToXML("curcolumn").x_str(),
Utf8ToXML(ecl::strf("%d",column)).x_str());
group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
return;
}
}
// if group does not exist add a new element
DOMElement * group = doc->createElement (Utf8ToXML("group").x_str());
group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(groupName).x_str());
group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
group->setAttribute(Utf8ToXML("curcolumn").x_str(),
Utf8ToXML(ecl::strf("%d",column)).x_str());
groupsElem->appendChild(group);
}
void StateManager::insertGroup(int pos, std::string groupName,
std::string indexName, std::string column) {
DOMElement * group = doc->createElement (Utf8ToXML("group").x_str());
group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(groupName).x_str());
group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str());
group->setAttribute(Utf8ToXML("curcolumn").x_str(), Utf8ToXML(column).x_str());
if (pos < 0 || pos >= groupList->getLength())
groupsElem->appendChild(group);
else {
DOMElement * nextGroup = dynamic_cast<DOMElement *>(groupList->item(pos));
groupsElem->insertBefore(group, nextGroup);
}
}
void StateManager::deleteGroup(std::string groupName) {
for (int i = 0, l = groupList->getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
groupsElem->removeChild(group);
return;
}
}
}
void StateManager::renameGroup(std::string oldName, std::string newName) {
// rename group element
for (int i = 0, l = groupList->getLength(); i < l; i++) {
DOMElement * group = dynamic_cast<DOMElement *>(groupList->item(i));
if (oldName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(newName).x_str());
break;
}
}
}
void StateManager::addIndex(std::string indexName, std::string &groupName,
double &location, int &curpos, int &curfirst) {
// check if index exists - get user attributes
for (int i = 0, l = indexList-> getLength(); i < l; i++) {
DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
groupName = XMLtoUtf8(index->getAttribute(Utf8ToXML("group").x_str())).c_str();
XMLDouble * result = new XMLDouble(index->getAttribute(Utf8ToXML("location").x_str()));
location = result->getValue();
delete result;
curpos = XMLString::parseInt(index->getAttribute(Utf8ToXML("curposition").x_str()));
curfirst = XMLString::parseInt(index->getAttribute(Utf8ToXML("curfirst").x_str()));
return;
}
}
// if index does not exist add a new element with default values
DOMElement * index = doc->createElement (Utf8ToXML("index").x_str());
index->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(indexName).x_str());
index->setAttribute(Utf8ToXML("group").x_str(), Utf8ToXML(groupName).x_str());
index->setAttribute(Utf8ToXML("location").x_str(), Utf8ToXML(ecl::strf("%g",location)).x_str());
index->setAttribute(Utf8ToXML("curfirst").x_str(), Utf8ToXML(ecl::strf("%d",0)).x_str());
index->setAttribute(Utf8ToXML("curposition").x_str(), Utf8ToXML(ecl::strf("%d",0)).x_str());
indicesElem->appendChild(index);
}
void StateManager::setIndexName(std::string oldName, std::string newName) {
// search index and set attribute
for (int i = 0, l = indexList-> getLength(); i < l; i++) {
DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
if (oldName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
index->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(newName).x_str());
return;
}
}
}
void StateManager::setIndexLocation(std::string indexName, double location) {
// search index and set attribute
for (int i = 0, l = indexList-> getLength(); i < l; i++) {
DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
index->setAttribute(Utf8ToXML("location").x_str(), Utf8ToXML(ecl::strf("%.15g",location)).x_str());
return;
}
}
}
void StateManager::setIndexCurpos(std::string indexName, int curpos) {
// search index and set attribute
for (int i = 0, l = indexList-> getLength(); i < l; i++) {
DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
index->setAttribute(Utf8ToXML("curposition").x_str(), Utf8ToXML(ecl::strf("%d",curpos)).x_str());
return;
}
}
}
void StateManager::setIndexCurfirst(std::string indexName, int curfirst) {
// search index and set attribute
for (int i = 0, l = indexList-> getLength(); i < l; i++) {
DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
index->setAttribute(Utf8ToXML("curfirst").x_str(), Utf8ToXML(ecl::strf("%d",curfirst)).x_str());
return;
}
}
}
void StateManager::setIndexGroup(std::string indexName, std::string groupName) {
// search index and set attribute
for (int i = 0, l = indexList-> getLength(); i < l; i++) {
DOMElement * index = dynamic_cast<DOMElement *>(indexList->item(i));
if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) {
index->setAttribute(Utf8ToXML("group").x_str(), Utf8ToXML(groupName).x_str());
return;
}
}
}
std::string StateManager::getAnnotation(std::string id) {
DOMElement * level = getLevel(id);
if (level != NULL)
return XMLtoUtf8(level->getAttribute(Utf8ToXML("annotation").x_str())).c_str();
else
return "";
}
void StateManager::setAnnotation(std::string id, std::string annotation) {
DOMElement * level = getLevel(id);
if (level == NULL) {
level = doc->createElement (Utf8ToXML("level").x_str());
level->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(id).x_str());
levelsElem->appendChild(level);
}
level->setAttribute(Utf8ToXML("annotation").x_str(), Utf8ToXML(annotation).x_str());
}
DOMElement * StateManager::getLevel(std::string id) {
XMLCh * xmlId = XMLString::replicate(Utf8ToXML(id).x_str());
bool levelFound = false;
DOMElement * level;
DOMNodeList * levelList = levelsElem->getElementsByTagName(Utf8ToXML("level").x_str());
for (int i = 0, l = levelList-> getLength(); i < l && !levelFound; i++) {
level = dynamic_cast<DOMElement *>(levelList->item(i));
if (XMLString::equals(xmlId,
level->getAttribute(Utf8ToXML("id").x_str()))) {
levelFound = true;
}
}
XMLString::release(&xmlId);
return levelFound ? level : NULL;
}
} // namespace enigma

View File

@@ -0,0 +1,83 @@
/*
* 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 STATEMGR_HH_INCLUDED
#define STATEMGR_HH_INCLUDED
#include "PropertyManager.hh"
#include <string>
#include <vector>
#include <xercesc/dom/DOMElement.hpp>
namespace enigma
{
/**
* A singelton manager for state info stored in XML format.
* <p>The singleton can be accessed via standard static instance() method or
* the application public ivar <code>app.state</code>.</p>
*
* <p>The storage location of the state is determined by the
* Application object.</p>
* <p>The set and get methods for properties take utf8 encoded property
* names and operate with utf8 encoded string values. Set methods create new
* state property elements if necessary. Get methods do not modify
* reference values and return C++ default values if the named property
* does not exist.</p>
*/
class StateManager : public PropertyManager {
public:
static StateManager *instance();
~StateManager();
virtual bool save();
void shutdown();
void getGroupNames(std::vector<std::string> *names);
std::string getGroupSelectedIndex(std::string groupName);
std::string getGroupSelectedColumn(std::string groupName);
void setGroupSelectedIndex(std::string groupName, std::string indexName);
void setGroupSelectedColumn(std::string groupName, std::string column);
void addGroup(std::string groupName, std::string indexName, int column); // update or append
void insertGroup(int pos, std::string groupName, std::string indexName, std::string column); // no duplicate check, pos -1 is append
void deleteGroup(std::string groupName);
void renameGroup(std::string oldName, std::string newName);
void addIndex(std::string indexName, std::string &groupName, double &location,
int &curpos, int &curfirst);
void setIndexName(std::string oldName, std::string newName);
void setIndexLocation(std::string indexName, double location);
void setIndexCurpos(std::string indexName, int curpos);
void setIndexCurfirst(std::string indexName, int curfirst);
void setIndexGroup(std::string indexName, std::string groupName);
std::string getAnnotation(std::string id);
void setAnnotation(std::string id, std::string annotation);
protected:
StateManager();
private:
static StateManager *theSingleton;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *groupsElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList *groupList;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *indicesElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList *indexList;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *levelsElem;
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *getLevel(std::string id);
};
} // namespace enigma
#endif

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005 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 <cstring>
#include "Utf8ToXML.hh"
#include "main.hh"
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/TransService.hpp>
XERCES_CPP_NAMESPACE_USE
namespace enigma
{
Utf8ToXML::Utf8ToXML(const char * const toTranscode) {
init(toTranscode);
}
Utf8ToXML::Utf8ToXML(const std::string * const toTranscode) {
init(toTranscode->c_str());
}
Utf8ToXML::Utf8ToXML(const std::string toTranscode) {
init(toTranscode.c_str());
}
void Utf8ToXML::init(const char * const toTranscode) {
#if _XERCES_VERSION >= 30000
XMLSize_t srcLength = std::strlen(toTranscode) + 1;
// make safe assumptions on utf-16 size
XMLSize_t maxDestLength = srcLength;
XMLSize_t charsEaten;
XMLSize_t destLength;
#else
unsigned int srcLength = std::strlen(toTranscode) + 1;
// make safe assumptions on utf-16 size
unsigned int maxDestLength = srcLength;
unsigned int charsEaten;
unsigned int destLength;
#endif
unsigned char *charSizes = new unsigned char[maxDestLength]; // just junk
// make a buffer - size does not matter - the object is temporary
xmlString = new XMLCh[maxDestLength];
// transcode to utf-8 -- there are no unrepresentable chars
destLength = app.xmlUtf8Transcoder->transcodeFrom((XMLByte *)toTranscode,
srcLength,
xmlString, maxDestLength,
charsEaten, charSizes);
delete[] charSizes;
if (charsEaten < srcLength)
// an assert - should never occur
Log << "Utf8toXML: incomplete transcoding - only "<< charsEaten <<
" of " << srcLength << "bytes were processed!" << std::endl;
}
Utf8ToXML::~Utf8ToXML() {
delete [] xmlString;
}
const XMLCh * Utf8ToXML::x_str() const {
return xmlString;
};
} //namespace enigma

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2005 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 ENIGMA_UTF8TOXML_HH
#define ENIGMA_UTF8TOXML_HH
#include <iostream>
#include <xercesc/util/XMLString.hpp>
namespace enigma
{
/**
* Transcoding utility for utf-8 strings to XMLCh strings
* Provides a simple interface for the memory management paradigm shift
* even though not terribly efficient. Make sure all Utf8toXML objects
* exist only temporarily, f.e.
* <code>DOMNode::setNodeValue(Utf8ToXML(char *toTranscode).x_str())</code> or
* <code>XMLString::replicate(Utf8ToXML(char *toTranscode).x_str())</code>
* Xerces should be initialized before using this class and all objects
* should be deleted before terminating.
*/
class Utf8ToXML {
public :
/**
* Makes a transcoding to XML.
*
* @param toTranscode utf-8 coded string
*/
Utf8ToXML(const char * const toTranscode);
/**
* Makes a transcoding to the local code page.
*
* @param toTranscode utf-8 coded string
*/
Utf8ToXML(const std::string * const toTranscode);
/**
* Makes a transcoding to the local code page.
*
* @param toTranscode utf-8 coded string
*/
Utf8ToXML(const std::string toTranscode);
~Utf8ToXML();
/**
* Returns the XML string. It remains owner of
* the string.
*/
const XMLCh * x_str() const;
private :
/**
* A XML copy. We are the owner.
*/
XMLCh * xmlString;
void init(const char * const toTranscode);
};
} //namespace enigma
#endif

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2005 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 "XMLtoLocal.hh"
XERCES_CPP_NAMESPACE_USE
namespace enigma
{
XMLtoLocal::XMLtoLocal(const XMLCh* const toTranscode) {
// Use XML for transcoding -- the returned string is owned by us
// but managed by XMLString!
localString = XMLString::transcode(toTranscode);
}
XMLtoLocal::~XMLtoLocal() {
XMLString::release(&localString);
}
const char* XMLtoLocal::c_str() const {
return localString;
}
} //namespace enigma

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2005 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 ENIGMA_XMLTOLOCAL_HH
#define ENIGMA_XMLTOLOCAL_HH
#include <iostream>
#include <xercesc/util/XMLString.hpp>
namespace enigma
{
/**
* Transcoding utility for XMLCh strings to local code page strings.
* Provides a simple interface for the memory management paradigm shift
* even though not terribly efficient. Make sure all XMLtoLocal objects
* exist only temporarily, f.e.
* <code>new String(XMLtoLocal(XMLCh *toTranscode).c_str())</code> or
* <code>Log << XMLtoLocal(XMLCh *toTranscode)</code>
* Xerces should be initialized before using this class and all objects
* should be deleted before terminating.
*/
class XMLtoLocal {
public :
/**
* Makes a transcoding to the local code page.
*
* @param toTranscode XML managed string
*/
XMLtoLocal(const XMLCh* const toTranscode);
~XMLtoLocal();
/**
* Returns the string coded in the local page. It remains owner of
* the string.
*/
const char* c_str() const;
private :
/**
* A copy coded in the local code page.
* The string is managed by XMLString - we are the owner.
*/
char* localString;
};
/**
* Enables efficient stream output of XMLCh * strings. Use it as
* <code>Log << XML2Local(XMLCh *toTranscode)</code>. All resources
* are managed and released.
*/
inline std::ostream& operator<<(std::ostream& target, const XMLtoLocal& toDump) {
target << toDump.c_str();
return target;
}
} //namespace enigma
#endif

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2005, 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 "XMLtoUtf8.hh"
#include "main.hh"
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/TransService.hpp>
XERCES_CPP_NAMESPACE_USE
namespace enigma
{
XMLtoUtf8::XMLtoUtf8(const XMLCh* const toTranscode) {
#if _XERCES_VERSION >= 30000
XMLSize_t srcLength = XMLString::stringLen(toTranscode) + 1;
// make safe assumptions on utf-8 size
XMLSize_t maxDestLength = 3 * srcLength;
XMLSize_t charsEaten;
XMLSize_t destLength;
#else
unsigned int srcLength = XMLString::stringLen(toTranscode) + 1;
// make safe assumptions on utf-8 size
unsigned int maxDestLength = 3 * srcLength;
unsigned int charsEaten;
unsigned int destLength;
#endif
// make a buffer - size does not matter - the object is temporary
utf8String = new char[maxDestLength];
// transcode to utf-8 -- there are no unrepresentable chars
destLength = app.xmlUtf8Transcoder->transcodeTo(toTranscode, srcLength,
(XMLByte *)utf8String, maxDestLength,
charsEaten, XMLTranscoder::UnRep_RepChar);
if (charsEaten < srcLength)
// an assert - should never occur
Log << "XMLtoUtf8: incomplete transcoding - only "<< charsEaten <<
" of " << srcLength << "characters were processed!" << std::endl;
}
XMLtoUtf8::~XMLtoUtf8() {
delete [] utf8String;
}
const char* XMLtoUtf8::c_str() const {
return utf8String;
};
} //namespace enigma

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2005 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 ENIGMA_XMLTOUTF8_HH
#define ENIGMA_XMLTOUTF8_HH
#include <iostream>
#include <xercesc/util/XMLString.hpp>
namespace enigma
{
/**
* Transcoding utility for XMLCh strings to utf-8 strings.
* Provides a simple interface for the memory management paradigm shift
* even though not terribly efficient. Make sure all XMLtoUtf8 objects
* exist only temporarily, f.e.
* <code>new string(XMLtoUtf8(XMLCh *toTranscode).c_str())</code>
* Xerces should be initialized before using this class and all objects
* should be deleted before terminating.
*/
class XMLtoUtf8 {
public :
/**
* Makes a transcoding to utf-8
*
* @param toTranscode XML managed string
*/
XMLtoUtf8(const XMLCh* const toTranscode);
~XMLtoUtf8();
/**
* Returns the string coded in utf-8. It remains owner of
* the string.
*/
const char * c_str() const;
private :
/**
* A copy coded in utf-8. We are the owner.
*/
char * utf8String;
};
} //namespace enigma
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 ACTORS_HH_INCLUDED
#define ACTORS_HH_INCLUDED
#include "objects_decl.hh"
namespace world
{
enum ActorID {
ac_INVALID = -1,
ac_FIRST = 0,
ac_blackball = 0,
ac_whiteball = 1,
ac_meditation = 2,
ac_killerball = 3,
ac_rotor = 4,
ac_top = 5,
ac_horse = 6,
ac_bug = 7,
ac_cannonball = 8,
ac_spermbird = 9,
ac_LAST = 9,
ac_COUNT
};
struct ActorTraits {
const char *name;
ActorID id;
float radius;
float default_mass;
};
/* -------------------- ActorInfo -------------------- */
struct Contact {
ecl::V2 pos;
ecl::V2 normal;
// Constructor
Contact (const ecl::V2 &pos_, const ecl::V2 &normal_)
: pos(pos_), normal (normal_) {}
};
typedef std::vector<Contact> ContactList;
/*!
* This class contains the information the physics engine
* maintains about dynamic objects ("actors").
*/
struct ActorInfo {
// ---------- Variables ----------
ecl::V2 pos; // Absolute position
ecl::V2 vel; // Velocity
ecl::V2 forceacc; // Force accumulator
double charge; // Electric charge
double mass; // Mass
double radius; // Radius of the ball
bool grabbed; // Actor not controlled by the physics engine
bool ignore_contacts; // Do not perform collision handling
// Variables used internally by the physics engine
ecl::V2 last_pos; // Position before current tick
ecl::V2 oldpos; // Backup position for enter/leave notification
ecl::V2 force; // Force used during tick
ecl::V2 collforce;
ContactList contacts;
ContactList new_contacts;
// Constructor
ActorInfo();
};
class Actor : public Object, public display::ModelCallback {
public:
// ModelCallback interface
void animcb ();
/* ---------- Object interface ---------- */
Actor *clone() = 0;
virtual Value message (const string &m, const Value &);
void set_attrib (const string& key, const Value &val);
/* ---------- Actor interface ---------- */
virtual const ActorTraits &get_traits() const = 0;
virtual void think (double dtime);
virtual void on_collision(Actor *a);
virtual void on_creation(const ecl::V2 &pos);
virtual void on_respawn (const ecl::V2 &pos);
virtual bool is_dead() const = 0;
virtual bool is_movable() const { return true; }
virtual bool is_flying() const { return false; }
virtual bool is_on_floor() const { return true; }
virtual bool is_drunken() const { return false; }
virtual bool is_invisible() const { return false; }
virtual bool can_drop_items() const { return false; }
virtual bool can_move() const;
virtual bool can_be_warped() const { return false; }
virtual bool has_shield() const { return false; }
virtual void init();
/* ---------- Methods ---------- */
void move ();
virtual void move_screen ();
void warp (const ecl::V2 &newpos);
bool sound_event (const char *name, double vol = 1.0);
void respawn();
void set_respawnpos(const ecl::V2& p);
void remove_respawnpos();
void find_respawnpos();
const ecl::V2 &get_respawnpos() const;
const ecl::V2 &get_startpos() const;
virtual void hide();
void show();
void add_force (const ecl::V2 &f);
/* ---------- Accessors ---------- */
ActorInfo *get_actorinfo();
const ActorInfo &get_actorinfo() const;
const ecl::V2 &get_pos() const;
const ecl::V2 &get_vel() const {
return m_actorinfo.vel;
}
bool has_spikes() const { return spikes; }
void set_spikes(bool has) { spikes = has; }
static double get_max_radius(); // max. radius of all actors
int get_controllers () const { return controllers; }
double get_mouseforce () const { return mouseforce; }
bool controlled_by(int player) const {
return (get_controllers() & (1+player)) != 0;
}
const GridPos &get_gridpos() const { return gridpos; }
protected:
Actor(const ActorTraits &tr);
void set_model (const string &modelname);
void set_anim (const string &modelname);
display::SpriteHandle &get_sprite() { return m_sprite; }
private:
/* ---------- Variables ---------- */
ActorInfo m_actorinfo;
display::SpriteHandle m_sprite;
ecl::V2 startingpos;
ecl::V2 respawnpos;
bool use_respawnpos;
bool spikes; // set by "it-pin"
int controllers;
double mouseforce;
GridPos gridpos;
};
inline ActorID get_id (Actor *a) {
return a->get_traits().id;
}
inline double get_radius (const Actor *a) {
return a->get_actorinfo().radius;
}
inline double get_mass (const Actor *a) {
return a->get_actorinfo().mass;
}
inline double get_charge (const Actor *a) {
return a->get_actorinfo().charge;
}
/* -------------------- Global Functions -------------------- */
void InitActors();
}
#endif

View File

@@ -0,0 +1,216 @@
namespace
{
// helper class to find good respawn positions
struct ExaminedLocation : public GridPos {
public:
ExaminedLocation(GridPos p) : GridPos(p) {}
bool operator<(const ExaminedLocation& other) const {
return (x == other.x) ? y<other.y : x<other.x;
}
};
typedef std::set<ExaminedLocation> ExaminedLocations;
class FreeRespawnLocationFinder
{
ExaminedLocations checked;
ExaminedLocations blocked;
ExaminedLocations candidates;
const Actor &actor_to_set;
bool actor_is_marble;
V2 preferred_position;
double max_enemy_gap;
V2 max_gap_pos;
static const double MAX_DISTANCE_WANTED;
static bool is_marble(const string& k) {
// true if kind 'k' is a marble
return k == "ac-blackball" || k == "ac-whiteball" || k == "ac-whiteball-small";
}
static bool is_respawn_floor(const string& k) {
// true if marble may appear on floors of kind 'k'
return
k != "fl-abyss" &&
k != "fl-water" &&
k != "fl-space"; // player cannot be moved on fl-space
}
static bool is_respawn_item(const string& k) {
// true if marble may appear on items of kind 'k'
return
k != "it-laserbeam" &&
k != "it-burnable-ignited";
}
static bool search_through_stone(const Stone& st) {
if (st.is_movable() || st.is_floating()) return true;
const string& k = st.get_kind();
return k == "st-puzzle";
}
static double wanted_distance_to(const string& k) {
// returns the size of the gap wanted between a marble and an actor of kind 'k'
if (k == "ac-rotor") return MAX_DISTANCE_WANTED;
if (k == "ac-top") return 3.0;
if (k == "ac-killerball" || k == "ac-bug") return 1.5;
return 0.3;
}
double distance_wanted_to(const Actor& a) {
double dist = 0.3;
if (actor_is_marble) dist = wanted_distance_to(a.get_kind());
else if (is_marble(a.get_kind())) dist = wanted_distance_to(actor_to_set.get_kind());
ASSERT(dist <= MAX_DISTANCE_WANTED, XLevelRuntime, "FreeRespawnLocationFinder: distance_wanted_to too large ");
return dist;
}
bool enemyActorAt(const V2& p) {
vector<Actor*> found_actors;
double range = get_radius (&actor_to_set) + MAX_DISTANCE_WANTED + Actor::get_max_radius();
if (GetActorsInRange(p, range, found_actors)) {
bool found_near_enemy = false;
double min_enemy_gap = 1000.0;
for (vector<Actor*>::const_iterator ai = found_actors.begin();
ai != found_actors.end();
++ai)
{
Actor *a = *ai;
if (a != &actor_to_set) {
double distance = length(p - a->get_pos());
double gap_between = distance - get_radius (&actor_to_set) - get_radius(a);
double wanted_gap = distance_wanted_to(*a);
if (gap_between < wanted_gap)
found_near_enemy = true;
if (gap_between < min_enemy_gap)
min_enemy_gap = gap_between;
}
}
if (found_near_enemy) {
if (min_enemy_gap<999.0) {
if (min_enemy_gap > max_enemy_gap) {
max_enemy_gap = min_enemy_gap;
max_gap_pos = p;
}
}
}
return found_near_enemy;
}
return false;
}
void examine(GridPos p) {
if (checked.find(p) != checked.end()) return; // already examined
checked.insert(p); // never check again
Floor *fl = GetFloor(p);
if (!fl || !is_respawn_floor(fl->get_kind())) return; // bad floor
bool may_respawn = true;
bool continue_search = true;
Item *it = GetItem(p);
if (it && !is_respawn_item(it->get_kind())) may_respawn = false; // bad item
Stone *st = GetStone(p);
if (st) {
if (!search_through_stone(*st)) continue_search = false;
may_respawn = false;
}
if (may_respawn) { // may be a candidate -> check for enemy actors
if (enemyActorAt(p.center())) may_respawn = false;
}
if (continue_search) blocked.insert(p);
if (may_respawn) candidates.insert(p);
}
public:
FreeRespawnLocationFinder(V2 p, const Actor& actor)
: actor_to_set(actor)
, preferred_position(p)
, max_enemy_gap(-1000.0)
{
actor_is_marble = is_marble(actor_to_set.get_kind());
ExaminedLocations affected; // all locations affected by current respawn position
{
double radius = get_radius (&actor_to_set);
int xmin = int(p[0]-radius);
int xmax = int(p[0]+radius);
int ymin = int(p[1]-radius);
int ymax = int(p[1]+radius);
for (int x = xmin; x <= xmax; ++x) {
for (int y = ymin; y <= ymax; ++y) {
affected.insert(GridPos(x, y));
}
}
}
for (ExaminedLocations::const_iterator ai = affected.begin(); ai != affected.end(); ++ai) {
examine(*ai);
}
if (candidates.size() != affected.size()) { // if any affected location may not be used for respawning
// choose alternate respawn location
blocked = affected; // start with all affected positions
while (candidates.empty()) {
ExaminedLocations curr_blocked;
swap(curr_blocked, blocked);
if (curr_blocked.empty()) {
break; // no chance to find a candidate
}
for (ExaminedLocations::const_iterator bl = curr_blocked.begin(); bl != curr_blocked.end(); ++bl) {
examine(move(*bl, NORTH));
examine(move(*bl, SOUTH));
examine(move(*bl, EAST));
examine(move(*bl, WEST));
}
}
if (candidates.empty()) { // no better location -> take least worse tested location
if (max_enemy_gap > 0.0) {
preferred_position = max_gap_pos;
}
}
else { // a better location has been found
ExaminedLocations::const_iterator c = candidates.begin();
advance(c, IntegerRand(0, int (candidates.size()-1)));
ASSERT(c != candidates.end(), XLevelRuntime, "FreeRespawnLocationFinder: list of candidates corrupt");
preferred_position = c->center();
}
}
}
V2 get_position() const { return preferred_position; }
};
}
namespace
{
const double FreeRespawnLocationFinder::MAX_DISTANCE_WANTED = 5.0;
}

View File

@@ -0,0 +1,970 @@
/*
* Copyright (C) 2004 Daniel Heck
* 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 "client.hh"
#include "game.hh"
#include "display.hh"
#include "options.hh"
#include "server.hh"
#include "gui/HelpMenu.hh"
#include "main.hh"
#include "gui/GameMenu.hh"
#include "sound.hh"
#include "player.hh"
#include "world.hh"
#include "nls.hh"
#include "StateManager.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
#include "lev/Proxy.hh"
#include "lev/RatingManager.hh"
#include "lev/ScoreManager.hh"
#include "ecl_sdl.hh"
#include "enet/enet.h"
#include <cctype>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace enigma::client;
using namespace ecl;
using namespace std;
#include "client_internal.hh"
/* -------------------- Auxiliary functions -------------------- */
namespace
{
/*! Display a message and change the current mouse speed. */
void set_mousespeed (double speed)
{
int s = round_nearest<int>(speed);
options::SetMouseSpeed (s);
s = round_nearest<int> (options::GetMouseSpeed ());
Msg_ShowText(strf(_("Mouse speed: %d"), s), false, 2.0);
}
/*! Generate the message that is displayed when the level starts. */
string displayedLevelInfo (lev::Proxy *level)
{
std::string text;
std::string tmp;
tmp = level->getLocalizedString("title");
if (tmp.empty())
tmp = _("Another nameless level");
text = string("\"")+ tmp +"\"";
tmp = level->getAuthor();
if (!tmp.empty())
text += _(" by ") + tmp;
tmp = level->getLocalizedString("subtitle");
if (!tmp.empty() && tmp != "subtitle")
text += string(" - \"")+ tmp + "\"";
tmp = level->getCredits(false);
if (!tmp.empty())
text += string(" - Credits: ")+ tmp;
tmp = level->getDedication(false);
if (!tmp.empty())
text += string(" - Dedication: ")+ tmp;
return text;
}
}
/* -------------------- Variables -------------------- */
namespace
{
Client client_instance;
const char HSEP = '^'; // history separator (use character that user cannot use)
}
#define CLIENT client_instance
/* -------------------- Client class -------------------- */
Client::Client()
: m_state (cls_idle),
m_levelname(),
m_hunt_against_time(0),
m_cheater(false),
m_user_input()
{
m_network_host = 0;
}
Client::~Client()
{
network_stop();
}
bool Client::network_start()
{
if (m_network_host)
return true;
m_network_host = enet_host_create (NULL,
1 /* only allow 1 outgoing connection */,
57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,
14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);
if (m_network_host == NULL) {
fprintf (stderr,
"An error occurred while trying to create an ENet client host.\n");
return false;
}
// ----- Connect to server
ENetAddress sv_address;
ENetPeer *m_server;
/* Connect to some.server.net:1234. */
enet_address_set_host (&sv_address, "localhost");
sv_address.port = 12345;
/* Initiate the connection, allocating the two channels 0 and 1. */
m_server = enet_host_connect (m_network_host, &sv_address, 2);
if (m_server == NULL) {
fprintf (stderr,
"No available peers for initiating an ENet connection.\n");
return false;
}
// Wait up to 5 seconds for the connection attempt to succeed.
ENetEvent event;
if (enet_host_service (m_network_host, &event, 5000) > 0 &&
event.type == ENET_EVENT_TYPE_CONNECT)
{
fprintf (stderr, "Connection to some.server.net:1234 succeeded.");
return true;
}
else
{
/* Either the 5 seconds are up or a disconnect event was */
/* received. Reset the peer in the event the 5 seconds */
/* had run out without any significant event. */
enet_peer_reset (m_server);
m_server = 0;
fprintf (stderr, "Connection to localhost:12345 failed.");
return false;
}
}
void Client::network_stop ()
{
if (m_network_host)
enet_host_destroy (m_network_host);
if (m_server)
enet_peer_reset (m_server);
}
/* ---------- Event handling ---------- */
void Client::handle_events()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_KEYDOWN:
on_keydown(e);
break;
case SDL_MOUSEMOTION:
if (abs(e.motion.xrel) > 300 || abs(e.motion.yrel) > 300) {
fprintf(stderr, "mouse event with %i, %i\n", e.motion.xrel, e.motion.yrel);
}
else
server::Msg_MouseForce (options::GetDouble("MouseSpeed") *
V2 (e.motion.xrel, e.motion.yrel));
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
on_mousebutton(e);
break;
case SDL_ACTIVEEVENT: {
update_mouse_button_state();
if (e.active.gain == 0 && !video::IsFullScreen())
show_menu();
break;
}
case SDL_VIDEOEXPOSE: {
display::RedrawAll(video::GetScreen());
break;
}
case SDL_QUIT:
client::Msg_Command("abort");
break;
}
}
}
void Client::update_mouse_button_state()
{
int b = SDL_GetMouseState(0, 0);
player::InhibitPickup((b & SDL_BUTTON(1)) || (b & SDL_BUTTON(3)));
}
void Client::on_mousebutton(SDL_Event &e)
{
if (e.button.state == SDL_PRESSED) {
if (e.button.button == 1) {
// left mousebutton -> activate first item in inventory
server::Msg_ActivateItem ();
}
else if (e.button.button == 3|| e.button.button == 4) {
// right mousebutton, wheel down -> rotate inventory
rotate_inventory(+1);
}
else if (e.button.button == 5) {
// wheel down -> inverse rotate inventory
rotate_inventory(-1);
}
}
update_mouse_button_state();
}
void Client::rotate_inventory (int direction)
{
m_user_input = "";
STATUSBAR->hide_text();
player::RotateInventory(direction);
}
/* -------------------- Console related -------------------- */
class HistoryProxy {
static int instances;
public:
static string content;
HistoryProxy();
~HistoryProxy() {
if (!--instances) app.state->setProperty("CommandHistory", content);
}
};
string HistoryProxy::content;
int HistoryProxy::instances = 0;
HistoryProxy::HistoryProxy() {
if (!instances++) {
content = app.state->getString("CommandHistory");
if (content.find(HSEP) == string::npos) content = string(1, HSEP);
}
}
static void user_input_history_append(const string& text, bool at_end = true) {
HistoryProxy history;
size_t old_pos = history.content.find(string(1, HSEP)+text+HSEP);
if (old_pos != string::npos)
history.content.erase(old_pos, text.length()+1);
if (at_end)
history.content += text+HSEP;
else
history.content = string(1, HSEP)+text+history.content;
}
void Client::process_userinput()
{
if (m_user_input != "") {
STATUSBAR->hide_text();
string commands = m_user_input;
user_input_history_append(m_user_input);
m_user_input = "";
size_t sep_pos;
while ((sep_pos = commands.find_first_of(';')) != string::npos) {
string first_command = commands.substr(0, sep_pos);
commands.erase(0, sep_pos+1);
server::Msg_Command (first_command);
}
server::Msg_Command (commands); // last command
}
}
void Client::user_input_append (char c) {
m_user_input += c;
Msg_ShowText (m_user_input, false);
}
void Client::user_input_backspace ()
{
if (!m_user_input.empty()) {
m_user_input.erase (m_user_input.size()-1, 1);
if (!m_user_input.empty()) {
// still not empty
Msg_ShowText (m_user_input, false);
} else {
// empty
STATUSBAR->hide_text();
}
}
}
void Client::user_input_previous ()
{
HistoryProxy history;
size_t last_start = history.content.find_last_of(HSEP, history.content.length()-2);
if (last_start != string::npos) {
string prev_input = history.content.substr(last_start+1, history.content.length()-last_start-2);
history.content.erase(last_start+1);
user_input_history_append(m_user_input, false);
m_user_input = prev_input;
if (m_user_input.empty())
STATUSBAR->hide_text();
else
Msg_ShowText (m_user_input, false);
}
}
void Client::user_input_next ()
{
HistoryProxy history;
size_t first_end = history.content.find_first_of(HSEP, 1);
if (first_end != string::npos) {
string next_input = history.content.substr(1, first_end-1);
history.content.erase(0, first_end);
user_input_history_append(m_user_input);
m_user_input = next_input;
if (m_user_input.empty())
STATUSBAR->hide_text();
else
Msg_ShowText (m_user_input, false);
}
}
void Client::on_keydown(SDL_Event &e)
{
SDLKey keysym = e.key.keysym.sym;
SDLMod keymod = e.key.keysym.mod;
if (keymod & KMOD_CTRL) {
switch (keysym) {
case SDLK_a:
server::Msg_Command ("restart");
break;
case SDLK_F3:
if (keymod & KMOD_SHIFT) {
// force a reload from file
lev::Proxy * curProxy = lev::Proxy::loadedLevel();
if (curProxy != NULL)
curProxy->release();
server::Msg_Command ("restart");
}
default:
break;
};
}
else if (keymod & KMOD_ALT) {
switch (keysym) {
case SDLK_x: abort(); break;
case SDLK_t:
if (enigma::WizardMode) {
Screen *scr = video::GetScreen();
ecl::TintRect(scr->get_surface (), display::GetGameArea(),
100, 100, 100, 0);
scr->update_all();
}
break;
case SDLK_s:
if (enigma::WizardMode) {
server::Msg_Command ("god");
}
break;
case SDLK_RETURN:
{
video::TempInputGrab (false);
video::ToggleFullscreen ();
sdl::FlushEvents();
}
break;
default:
break;
};
}
else {
switch (keysym) {
case SDLK_ESCAPE: show_menu(); break;
case SDLK_LEFT: set_mousespeed(options::GetMouseSpeed() - 1); break;
case SDLK_RIGHT: set_mousespeed(options::GetMouseSpeed() + 1); break;
case SDLK_TAB: rotate_inventory(+1); break;
case SDLK_F1: show_help(); break;
case SDLK_F2:
// display hint
break;
case SDLK_F3:
if (keymod & KMOD_SHIFT)
server::Msg_Command ("restart");
else
server::Msg_Command ("suicide");
break;
case SDLK_F4: Msg_AdvanceLevel(lev::ADVANCE_STRICTLY); break;
case SDLK_F5: Msg_AdvanceLevel(lev::ADVANCE_UNSOLVED); break;
case SDLK_F6: Msg_JumpBack(); break;
case SDLK_F10: {
lev::Proxy *level = lev::Proxy::loadedLevel();
std::string basename = std::string("screenshots/") +
level->getLocalSubstitutionLevelPath();
std::string fname = basename + ".png";
std::string fullPath;
int i = 1;
while (app.resourceFS->findFile(fname, fullPath)) {
fname = basename + ecl::strf("#%d", i++) + ".png";
}
std::string savePath = app.userImagePath + "/" + fname;
video::Screenshot(savePath);
break;
}
case SDLK_RETURN: process_userinput(); break;
case SDLK_BACKSPACE: user_input_backspace(); break;
case SDLK_UP: user_input_previous(); break;
case SDLK_DOWN: user_input_next(); break;
default:
if (e.key.keysym.unicode && (e.key.keysym.unicode & 0xff80) == 0) {
char ascii = static_cast<char>(e.key.keysym.unicode & 0x7f);
if (isalnum (ascii) ||
strchr(" .-!\"$%&/()=?{[]}\\#'+*~_,;.:<>|", ascii)) // don't add '^' or change history code
{
user_input_append(ascii);
}
}
break;
}
}
}
static const char *helptext_ingame[] = {
N_("Left mouse button:"), N_("Activate/drop leftmost inventory item"),
N_("Right mouse button:"), N_("Rotate inventory items"),
N_("Escape:"), N_("Show game menu"),
N_("F1:"), N_("Show this help"),
N_("F3:"), N_("Kill current marble"),
N_("Shift+F3:"), N_("Restart the current level"),
N_("F4:"), N_("Skip to next level"),
N_("F5:"), 0, // see below
N_("F6:"), N_("Jump back to last level"),
N_("F10:"), N_("Make screenshot"),
N_("Left/right arrow:"), N_("Change mouse speed"),
N_("Alt+x:"), N_("Return to level menu"),
// N_("Alt+Return:"), N_("Switch between fullscreen and window"),
0
};
void Client::show_help()
{
server::Msg_Pause (true);
video::TempInputGrab grab(false);
helptext_ingame[15] = app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST
? _("Skip to next level for best score hunt")
: _("Skip to next unsolved level");
video::ShowMouse();
gui::displayHelp(helptext_ingame, 200);
video::HideMouse();
update_mouse_button_state();
if (m_state == cls_game)
display::RedrawAll(video::GetScreen());
server::Msg_Pause (false);
game::ResetGameTimer();
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST)
server::Msg_Command ("restart"); // inhibit cheating
}
void Client::show_menu()
{
server::Msg_Pause (true);
ecl::Screen *screen = video::GetScreen();
video::TempInputGrab grab (false);
video::ShowMouse();
{
int x, y;
display::GetReferencePointCoordinates(&x, &y);
enigma::gui::GameMenu(x, y).manage();
}
video::HideMouse();
update_mouse_button_state();
if (m_state == cls_game)
display::RedrawAll(screen);
server::Msg_Pause (false);
game::ResetGameTimer();
}
void Client::draw_screen()
{
switch (m_state) {
case cls_error: {
Screen *scr = video::GetScreen();
GC gc (scr->get_surface());
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
vector<string> lines;
ecl::split_copy (m_error_message, '\n', back_inserter(lines));
int x = 60;
int y = 60;
int yskip = 25;
const video::VMInfo *vminfo = video::GetInfo();
int width = vminfo->width - 120;
for (unsigned i=0; i<lines.size(); ) {
std::string::size_type breakPos = ecl::breakString (f, lines[i],
" ", width);
f->render(gc, x, y, lines[i].substr(0,breakPos).c_str());
y += yskip;
if (breakPos != lines[i].size()) {
// process rest of line
lines[i] = lines[i].substr(breakPos);
} else {
// process next line
i++;
}
}
scr->update_all();
scr->flush_updates();
break;
}
default:
break;
}
}
std::string Client::init_hunted_time()
{
std::string hunted;
m_hunt_against_time = 0;
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) {
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
if (best_user_time>0 && (wr_time == -1 || best_user_time<wr_time)) {
m_hunt_against_time = best_user_time;
hunted = "you";
}
else if (wr_time>0) {
m_hunt_against_time = wr_time;
hunted = ratingMgr->getBestScoreHolder(curProxy, difficulty);
}
// STATUSBAR->set_timerstart(-m_hunt_against_time);
}
return hunted;
}
void Client::tick (double dtime)
{
const double timestep = 0.01; // 10ms
switch (m_state) {
case cls_idle:
break;
case cls_preparing_game: {
video::TransitionEffect *fx = m_effect.get();
if (fx && !fx->finished()) {
fx->tick (dtime);
}
else {
m_effect.reset();
server::Msg_StartGame();
m_state = cls_game;
m_timeaccu = 0;
m_total_game_time = 0;
sdl::FlushEvents();
}
break;
}
case cls_game:
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) {
int old_second = round_nearest<int> (m_total_game_time);
int second = round_nearest<int> (m_total_game_time + dtime);
if (m_hunt_against_time && old_second <= m_hunt_against_time) {
if (second > m_hunt_against_time) { // happens exactly once when par has passed by
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
string message;
if (wr_time>0 && (best_user_time<0 || best_user_time>wr_time)) {
message = string(_("Too slow for ")) +
ratingMgr->getBestScoreHolder(curProxy, difficulty) +
".. [Ctrl-A]";
}
else {
message = string(_("You are slow today.. [Ctrl-A]"));
}
client::Msg_PlaySound("shatter", 1.0);
Msg_ShowText(message, true, 2.0);
}
else {
if (old_second<second && // tick every second
(second >= (m_hunt_against_time-5) || // at least 5 seconds
second >= round_nearest<int> (m_hunt_against_time*.8))) // or the last 20% before par
{
client::Msg_PlaySound("pickup", 1.0);
}
}
}
}
m_total_game_time += dtime;
STATUSBAR->set_time (m_total_game_time);
// fall through
case cls_finished: {
m_timeaccu += dtime;
for (;m_timeaccu >= timestep; m_timeaccu -= timestep) {
display::Tick (timestep);
}
display::Redraw(video::GetScreen());
handle_events();
break;
}
case cls_gamemenu:
break;
case cls_gamehelp:
break;
case cls_abort:
break;
case cls_error:
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_KEYDOWN:
case SDL_QUIT:
client::Msg_Command("abort");
break;
}
}
}
break;
}
}
void Client::level_finished()
{
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int difficulty = app.state->getInt("Difficulty");
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
string par_name = ratingMgr->getBestScoreHolder(curProxy, difficulty);
int par_time = ratingMgr->getParScore(curProxy, difficulty);
int level_time = round_nearest<int> (m_total_game_time);
string text;
bool timehunt_restart = false;
if (wr_time > 0) {
if (best_user_time<0 || best_user_time>wr_time) {
if (level_time == wr_time)
text = string(_("Exactly the world record of "))+par_name+"!";
else if (level_time<wr_time)
text = _("Great! A new world record!");
}
}
if (text.length() == 0 && best_user_time>0) {
if (level_time == best_user_time) {
text = _("Again your personal record...");
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST)
timehunt_restart = true; // when hunting yourself: Equal is too slow
}
else if (level_time<best_user_time)
if (par_time >= 0 && level_time <= par_time)
text = _("New personal record - better than par!");
else if (par_time >= 0)
text = _("New personal record, but over par!");
else
text = _("New personal record!");
}
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST &&
(wr_time>0 || best_user_time>0))
{
bool with_par = best_user_time == -1 || (wr_time >0 && wr_time<best_user_time);
int behind = level_time - (with_par ? wr_time : best_user_time);
if (behind>0) {
if (best_user_time>0 && level_time<best_user_time && with_par) {
text = _("Your record, ");
}
else {
text = "";
}
text += strf("%d:%02d ", static_cast<int> (behind/60)%100, behind%60);
if (with_par)
text += _("behind world record.");
else
text += _("behind your record.");
timehunt_restart = true; // time hunt failed -> repeat level
}
}
if (text.length() == 0) {
if (par_time >= 0 && level_time <= par_time)
text = _("Level finished - better than par!");
else if (par_time >= 0)
text = _("Level finished, but over par!");
else
text = _("Level finished!");
}
if (m_cheater)
text += _(" Cheater!");
Msg_ShowText (text, false);
if (!m_cheater) {
scm->updateUserScore(curProxy, difficulty, level_time);
// save score (just in case Enigma crashes when loading next level)
lev::ScoreManager::instance()->save();
}
if (timehunt_restart)
server::Msg_Command("restart");
else
m_state = cls_finished;
}
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
void Client::level_loaded(bool isRestart)
{
lev::Index *ind = lev::Index::getCurrentIndex();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
// update window title
video::SetCaption(ecl::strf(_("Enigma pack %s - level #%d: %s"), ind->getName().c_str(),
ind->getCurrentLevel(), curProxy->getTitle().c_str()).c_str());
string hunted = init_hunted_time(); // sets m_hunt_against_time (used below)
// show level information (name, author, etc.)
{
string displayed_info = "";
if (m_hunt_against_time>0) {
if (hunted == "you")
displayed_info = _("Your record: ");
else
displayed_info = _("World record to beat: ");
displayed_info += ecl::strf("%d:%02d", (m_hunt_against_time/60)%100,
m_hunt_against_time%60);
//+ _(" by ") +hunted;
// makes the string too long in many levels
Msg_ShowText (displayed_info, true, 4.0);
}
else {
displayed_info = displayedLevelInfo(curProxy);
Msg_ShowText (displayed_info, true, 2.0);
}
}
sound::FadeoutMusic();
if (options::GetBool("InGameMusic")) {
sound::PlayMusic (options::GetString("LevelMusicFile"));
} else {
sound::StopMusic();
}
// start screen transition
GC gc(video::BackBuffer());
display::DrawAll(gc);
m_effect.reset (video::MakeEffect ((isRestart ? video::TM_SQUARES :
video::TM_PUSH_RANDOM), video::BackBuffer()));
m_cheater = false;
m_state = cls_preparing_game;
}
void Client::handle_message (Message *m) { // @@@ unused
switch (m->type) {
case CLMSG_LEVEL_LOADED:
break;
default:
fprintf (stderr, "Unhandled client event: %d\n", m->type);
break;
}
}
void Client::error (const string &text)
{
m_error_message = text;
m_state = cls_error;
draw_screen();
}
/* -------------------- Functions -------------------- */
bool client::NetworkStart()
{
return CLIENT.network_start();
}
void client::Msg_LevelLoaded(bool isRestart)
{
CLIENT.level_loaded(isRestart);
}
void client::Tick (double dtime) {
CLIENT.tick (dtime);
sound::Tick (dtime);
}
void client::Stop() {
CLIENT.stop ();
}
void client::Msg_AdvanceLevel (lev::LevelAdvanceMode mode) {
lev::Index *ind = lev::Index::getCurrentIndex();
// log last played level
lev::PersistentIndex::addCurrentToHistory();
if (ind->advanceLevel(mode)) {
// now we may advance
server::Msg_LoadLevel(ind->getCurrent(), false);
}
else
client::Msg_Command("abort");
}
void client::Msg_JumpBack() {
// log last played level
lev::PersistentIndex::addCurrentToHistory();
server::Msg_JumpBack();
}
bool client::AbortGameP() {
return CLIENT.abort_p();
}
void client::Msg_Command(const string& cmd) {
if (cmd == "abort") {
CLIENT.abort();
}
else if (cmd == "level_finished") {
client::Msg_PlaySound("finished", 1.0);
CLIENT.level_finished();
}
else if (cmd == "cheater") {
CLIENT.mark_cheater();
}
else if (cmd == "easy_going") {
CLIENT.easy_going();
}
else {
enigma::Log << "Warning: Client received unknown command '" << cmd << "'\n";
}
}
void client::Msg_PlayerPosition (unsigned iplayer, const V2 &pos)
{
if (iplayer == (unsigned)player::CurrentPlayer()) {
sound::SetListenerPosition (pos);
display::SetReferencePoint (pos);
}
}
void client::Msg_PlaySound (const std::string &wavfile,
const ecl::V2 &pos,
double relative_volume)
{
sound::EmitSoundEvent (wavfile.c_str(), pos, relative_volume);
}
void client::Msg_PlaySound (const std::string &wavfile, double relative_volume)
{
sound::EmitSoundEvent (wavfile.c_str(), V2(), relative_volume);
}
void client::Msg_Sparkle (const ecl::V2 &pos) {
display::AddEffect (pos, "ring-anim");
}
void client::Msg_ShowText
(const std::string &text, bool scrolling, double duration)
{
STATUSBAR->show_text (text, scrolling, duration);
}
void client::Msg_Error (const std::string &text)
{
CLIENT.error (text);
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2003,2004,2005 Daniel Heck
*
* 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 CLIENT_HH_INCLUDED
#define CLIENT_HH_INCLUDED
#include "lev/Index.hh"
namespace enigma_client
{
using namespace enigma;
/* -------------------- Functions -------------------- */
bool NetworkStart();
void Tick (double dtime);
bool AbortGameP();
void Stop();
/* -------------------- Server->Client messages -------------------- */
void Msg_Command(const std::string& cmd);
void Msg_AdvanceLevel (lev::LevelAdvanceMode mode);
void Msg_JumpBack();
void Msg_LevelLoaded(bool isRestart);
void Msg_PlayerPosition (unsigned iplayer, const ecl::V2 &pos);
void Msg_Sparkle (const ecl::V2 &pos);
void Msg_ShowText (const std::string &text, bool scrolling, double duration=-1);
//! Play sound emanating from an absolute position in the world
void Msg_PlaySound (const std::string &soundname,
const ecl::V2 &pos,
double relative_volume);
//! Play a sound emanating from the player's current position
void Msg_PlaySound (const std::string &soundname, double relative_volume);
//! Stop the game and display an error message instead
void Msg_Error (const std::string &text);
}
namespace enigma
{
namespace client = enigma_client;
}
#endif

View File

@@ -0,0 +1,172 @@
#include "gui/Menu.hh"
#include "video.hh"
#include "ecl_buffer.hh"
namespace
{
using ecl::Buffer;
using std::string;
/* -------------------- Server -> Client messages -------------------- */
enum ClientCommand {
CLMSG_NOOP,
CLMSG_NEW_WORLD,
CLMSG_LEVEL_LOADED,
CLMSG_CHANGE_FIELD,
CLMSG_ADD_ACTOR,
CLMSG_MOVE_ACTOR,
CLMSG_FOCUS_ACTOR,
CLMSG_CHANGE_LINE,
CLMSG_PLAY_SOUND,
CLMSG_SHOW_TEXT,
CLMSG_ERROR // error occurred
};
struct Message {
Message (ClientCommand type_ = CLMSG_NOOP) : type (type_) {
}
ClientCommand type;
};
struct Cl_NewWorld {
std::string levelname;
int width;
int height;
};
struct Cl_LevelLoaded : public Message {
Cl_LevelLoaded() : Message (CLMSG_LEVEL_LOADED) {
}
};
Buffer &operator << (Buffer &b, const Cl_LevelLoaded &m) {
return b << Uint8 (CLMSG_LEVEL_LOADED);
}
struct Cl_ChangeField {
};
struct Cl_AddActor {
};
struct Cl_MoveActor {
};
struct Cl_FocusActor {
};
struct Cl_ShowText : public Message {
Cl_ShowText() : Message(CLMSG_SHOW_TEXT) {
}
string text;
float duration;
bool scrolling;
bool interruptible;
};
struct Cl_AddEffect {
float x, y;
};
struct Cl_PlaySound {
string soundname;
float x, y;
int priority;
};
/* -------------------- Client class -------------------- */
enum ClientState {
cls_idle,
cls_preparing_game, // level loaded, currently updating the screen
cls_game,
cls_finished, // level finished, waiting for next one
cls_gamehelp,
cls_gamemenu,
cls_abort,
cls_error
};
class Client {
public:
Client();
~Client();
void tick (double dtime);
void stop() { m_state = cls_idle; }
bool network_start();
void network_stop();
void handle_message(Message *msg);
void level_loaded(bool isRestart);
void level_finished();
void error (const std::string &text);
void abort() { m_state = cls_abort; }
bool abort_p() const { return m_state == cls_abort; }
void mark_cheater() { m_cheater = true; }
void easy_going() { m_hunt_against_time = false; }
private:
std::string init_hunted_time();
/* ---------- Private methods ---------- */
void show_menu();
void show_help();
// Screen update (state dependant)
void draw_screen();
// Event handling
void handle_events();
void on_keydown(SDL_Event &e);
void on_mousebutton(SDL_Event &e);
void update_mouse_button_state();
// Inventory & command line
void rotate_inventory(int direction);
void process_userinput();
void user_input_append (char c);
void user_input_backspace ();
void user_input_previous ();
void user_input_next ();
// Variables
ClientState m_state;
string m_levelname;
double m_timeaccu;
double m_total_game_time;
int m_hunt_against_time;
bool m_cheater;
string m_user_input;
string m_error_message;
std::auto_ptr<video::TransitionEffect> m_effect;
ENetHost *m_network_host;
ENetPeer *m_server;
private:
Client (const Client&);
Client &operator = (const Client &);
};
}

View File

@@ -0,0 +1,323 @@
/* src/config.h. Generated from config.h.in by configure. */
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* android paths */
#define SYSTEM_DATA_DIR "."
#define LOCALEDIR "."
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
/* #undef CRAY_STACKSEG_END */
/* Define to 1 if using `alloca.c'. */
/* #undef C_ALLOCA */
/* Include experimental features */
/* #undef ENABLE_EXPERIMENTAL */
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#define ENABLE_NLS 1
/* Define to 1 if you have `alloca', as a function or macro. */
#define HAVE_ALLOCA 1
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
#define HAVE_ALLOCA_H 1
/* Define to 1 if you have the <argz.h> header file. */
/* #undef HAVE_ARGZ_H */
/* Define to 1 if you have the `asprintf' function. */
#define HAVE_ASPRINTF 1
/* Define if the GNU dcgettext() function is already present or preinstalled.
*/
/* #undef HAVE_DCGETTEXT */
/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you
don't. */
#define HAVE_DECL_FEOF_UNLOCKED 0
/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
you don't. */
#define HAVE_DECL_FGETS_UNLOCKED 0
/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you
don't. */
#define HAVE_DECL_GETC_UNLOCKED 1
/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you
don't. */
#define HAVE_DECL__SNPRINTF 0
/* Define to 1 if you have the declaration of `_snwprintf', and to 0 if you
don't. */
#define HAVE_DECL__SNWPRINTF 0
/* Define to 1 if you have the <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define to 1 if you have the `fwprintf' function. */
#define HAVE_FWPRINTF 1
/* Define to 1 if you have the `getcwd' function. */
#define HAVE_GETCWD 1
/* Define to 1 if you have the `getegid' function. */
#define HAVE_GETEGID 1
/* Define to 1 if you have the `geteuid' function. */
#define HAVE_GETEUID 1
/* Define to 1 if you have the `getgid' function. */
#define HAVE_GETGID 1
/* Define to 1 if you have the `getpagesize' function. */
/* #undef HAVE_GETPAGESIZE */
/* Define if the GNU gettext() function is already present or preinstalled. */
/* #undef HAVE_GETTEXT */
/* Define to 1 if you have the `getuid' function. */
#define HAVE_GETUID 1
/* Define if you have the iconv() function. */
#define HAVE_ICONV 1
/* Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>. */
#define HAVE_INTMAX_T 1
/* Define if <inttypes.h> exists and doesn't clash with <sys/types.h>. */
#define HAVE_INTTYPES_H 1
/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
declares uintmax_t. */
#define HAVE_INTTYPES_H_WITH_UINTMAX 1
/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
/* #undef HAVE_LANGINFO_CODESET */
/* Define if your <locale.h> file defines LC_MESSAGES. */
#define HAVE_LC_MESSAGES 1
/* Define to 1 if you have the `png' library (-lpng). */
#define HAVE_LIBPNG 1
/* Define to 1 if you have the `png12' library (-lpng12). */
/* #undef HAVE_LIBPNG12 */
/* Define to 1 if you have the `SDL_image' library (-lSDL_image). */
#define HAVE_LIBSDL_IMAGE 1
/* Define to 1 if you have the `SDL_mixer' library (-lSDL_mixer). */
#define HAVE_LIBSDL_MIXER 1
/* Define to 1 if you have the `SDL_ttf' library (-lSDL_ttf). */
#define HAVE_LIBSDL_TTF 1
/* Define to 1 if you have the `winmm' library (-lwinmm). */
/* #undef HAVE_LIBWINMM */
/* Define to 1 if you have the `xerces-c' library (-lxerces-c). */
#define HAVE_LIBXERCES_C 1
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define to 1 if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1
/* Define if you have the 'long double' type. */
#define HAVE_LONG_DOUBLE 1
/* Define if you have the 'long long' type. */
#define HAVE_LONG_LONG 1
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `mempcpy' function. */
/* #undef HAVE_MEMPCPY */
/* Define to 1 if you have a working `mmap' system call. */
/* #undef HAVE_MMAP */
/* Define to 1 if you have the `munmap' function. */
#define HAVE_MUNMAP 1
/* Define to 1 if you have the <nl_types.h> header file. */
/* #undef HAVE_NL_TYPES_H */
/* Define if your printf() function supports format strings with positions. */
#define HAVE_POSIX_PRINTF 1
/* Define to 1 if you have the `putenv' function. */
#define HAVE_PUTENV 1
/* Define to 1 if you have the `setenv' function. */
#define HAVE_SETENV 1
/* Define to 1 if you have the `setlocale' function. */
#define HAVE_SETLOCALE 1
/* Define to 1 if you have the `snprintf' function. */
#define HAVE_SNPRINTF 1
/* Define to 1 if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
uintmax_t. */
#define HAVE_STDINT_H_WITH_UINTMAX 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `stpcpy' function. */
/* #undef HAVE_STPCPY */
/* Define to 1 if you have the `strcasecmp' function. */
#define HAVE_STRCASECMP 1
/* Define to 1 if you have the `strdup' function. */
#define HAVE_STRDUP 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strtoul' function. */
#define HAVE_STRTOUL 1
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the `tsearch' function. */
/* #undef HAVE_TSEARCH */
/* Define if you have the 'uintmax_t' type in <stdint.h> or <inttypes.h>. */
#define HAVE_UINTMAX_T 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if you have the 'unsigned long long' type. */
#define HAVE_UNSIGNED_LONG_LONG 1
/* Define if you have the 'wchar_t' type. */
#define HAVE_WCHAR_T 1
/* Define to 1 if you have the `wcslen' function. */
#define HAVE_WCSLEN 1
/* Define if you have the 'wint_t' type. */
#define HAVE_WINT_T 1
/* Define to 1 if you have the `__argz_count' function. */
/* #undef HAVE___ARGZ_COUNT */
/* Define to 1 if you have the `__argz_next' function. */
/* #undef HAVE___ARGZ_NEXT */
/* Define to 1 if you have the `__argz_stringify' function. */
/* #undef HAVE___ARGZ_STRINGIFY */
/* Define to 1 if you have the `__fsetlocking' function. */
/* #undef HAVE___FSETLOCKING */
/* Define as const if the declaration of iconv() needs const. */
#define ICONV_CONST const
/* Define if integer division by zero raises signal SIGFPE. */
#define INTDIV0_RAISES_SIGFPE 0
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O */
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "enigma"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "enigma 1.01"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "enigma"
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.01"
/* Define if <inttypes.h> exists and defines unusable PRI* macros. */
/* #undef PRI_MACROS_BROKEN */
/* Whether SDL_image library provides init */
#define SDL_IMG_INIT
/* Whether SDL_mixer library provides init */
#define SDL_MIX_INIT
/* Define as the maximum value of type 'size_t', if the system doesn't define
it. */
/* #undef SIZE_MAX */
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
automatically deduced at runtime.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
/* #undef STACK_DIRECTION */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif
/* Define to `long int' if <sys/types.h> does not define. */
/* #undef off_t */
/* Define as the type of the result of subtracting two pointers, if the system
doesn't define it. */
/* #undef ptrdiff_t */
/* Define to empty if the C compiler doesn't support this keyword. */
/* #undef signed */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to unsigned long or unsigned long long if <stdint.h> and
<inttypes.h> don't define. */
/* #undef uintmax_t */

View File

@@ -0,0 +1,317 @@
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
#undef CRAY_STACKSEG_END
/* Define to 1 if using `alloca.c'. */
#undef C_ALLOCA
/* Include experimental features */
#undef ENABLE_EXPERIMENTAL
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#undef ENABLE_NLS
/* Define to 1 if you have `alloca', as a function or macro. */
#undef HAVE_ALLOCA
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
#undef HAVE_ALLOCA_H
/* Define to 1 if you have the <argz.h> header file. */
#undef HAVE_ARGZ_H
/* Define to 1 if you have the `asprintf' function. */
#undef HAVE_ASPRINTF
/* Define if the GNU dcgettext() function is already present or preinstalled.
*/
#undef HAVE_DCGETTEXT
/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you
don't. */
#undef HAVE_DECL_FEOF_UNLOCKED
/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
you don't. */
#undef HAVE_DECL_FGETS_UNLOCKED
/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you
don't. */
#undef HAVE_DECL_GETC_UNLOCKED
/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you
don't. */
#undef HAVE_DECL__SNPRINTF
/* Define to 1 if you have the declaration of `_snwprintf', and to 0 if you
don't. */
#undef HAVE_DECL__SNWPRINTF
/* Define to 1 if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define to 1 if you have the `fwprintf' function. */
#undef HAVE_FWPRINTF
/* Define to 1 if you have the `getcwd' function. */
#undef HAVE_GETCWD
/* Define to 1 if you have the `getegid' function. */
#undef HAVE_GETEGID
/* Define to 1 if you have the `geteuid' function. */
#undef HAVE_GETEUID
/* Define to 1 if you have the `getgid' function. */
#undef HAVE_GETGID
/* Define to 1 if you have the `getpagesize' function. */
#undef HAVE_GETPAGESIZE
/* Define if the GNU gettext() function is already present or preinstalled. */
#undef HAVE_GETTEXT
/* Define to 1 if you have the `getuid' function. */
#undef HAVE_GETUID
/* Define if you have the iconv() function. */
#undef HAVE_ICONV
/* Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>. */
#undef HAVE_INTMAX_T
/* Define if <inttypes.h> exists and doesn't clash with <sys/types.h>. */
#undef HAVE_INTTYPES_H
/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
declares uintmax_t. */
#undef HAVE_INTTYPES_H_WITH_UINTMAX
/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
#undef HAVE_LANGINFO_CODESET
/* Define if your <locale.h> file defines LC_MESSAGES. */
#undef HAVE_LC_MESSAGES
/* Define to 1 if you have the `png' library (-lpng). */
#undef HAVE_LIBPNG
/* Define to 1 if you have the `png12' library (-lpng12). */
#undef HAVE_LIBPNG12
/* Define to 1 if you have the `SDL_image' library (-lSDL_image). */
#undef HAVE_LIBSDL_IMAGE
/* Define to 1 if you have the `SDL_mixer' library (-lSDL_mixer). */
#undef HAVE_LIBSDL_MIXER
/* Define to 1 if you have the `SDL_ttf' library (-lSDL_ttf). */
#undef HAVE_LIBSDL_TTF
/* Define to 1 if you have the `winmm' library (-lwinmm). */
#undef HAVE_LIBWINMM
/* Define to 1 if you have the `xerces-c' library (-lxerces-c). */
#undef HAVE_LIBXERCES_C
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define if you have the 'long double' type. */
#undef HAVE_LONG_DOUBLE
/* Define if you have the 'long long' type. */
#undef HAVE_LONG_LONG
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `mempcpy' function. */
#undef HAVE_MEMPCPY
/* Define to 1 if you have a working `mmap' system call. */
#undef HAVE_MMAP
/* Define to 1 if you have the `munmap' function. */
#undef HAVE_MUNMAP
/* Define to 1 if you have the <nl_types.h> header file. */
#undef HAVE_NL_TYPES_H
/* Define if your printf() function supports format strings with positions. */
#undef HAVE_POSIX_PRINTF
/* Define to 1 if you have the `putenv' function. */
#undef HAVE_PUTENV
/* Define to 1 if you have the `setenv' function. */
#undef HAVE_SETENV
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
/* Define to 1 if you have the <stddef.h> header file. */
#undef HAVE_STDDEF_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
uintmax_t. */
#undef HAVE_STDINT_H_WITH_UINTMAX
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `stpcpy' function. */
#undef HAVE_STPCPY
/* Define to 1 if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define to 1 if you have the `strdup' function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `strtoul' function. */
#undef HAVE_STRTOUL
/* Define to 1 if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the `tsearch' function. */
#undef HAVE_TSEARCH
/* Define if you have the 'uintmax_t' type in <stdint.h> or <inttypes.h>. */
#undef HAVE_UINTMAX_T
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the 'unsigned long long' type. */
#undef HAVE_UNSIGNED_LONG_LONG
/* Define if you have the 'wchar_t' type. */
#undef HAVE_WCHAR_T
/* Define to 1 if you have the `wcslen' function. */
#undef HAVE_WCSLEN
/* Define if you have the 'wint_t' type. */
#undef HAVE_WINT_T
/* Define to 1 if you have the `__argz_count' function. */
#undef HAVE___ARGZ_COUNT
/* Define to 1 if you have the `__argz_next' function. */
#undef HAVE___ARGZ_NEXT
/* Define to 1 if you have the `__argz_stringify' function. */
#undef HAVE___ARGZ_STRINGIFY
/* Define to 1 if you have the `__fsetlocking' function. */
#undef HAVE___FSETLOCKING
/* Define as const if the declaration of iconv() needs const. */
#undef ICONV_CONST
/* Define if integer division by zero raises signal SIGFPE. */
#undef INTDIV0_RAISES_SIGFPE
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
#undef NO_MINUS_C_MINUS_O
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define if <inttypes.h> exists and defines unusable PRI* macros. */
#undef PRI_MACROS_BROKEN
/* Whether SDL_image library provides init */
#undef SDL_IMG_INIT
/* Whether SDL_mixer library provides init */
#undef SDL_MIX_INIT
/* Define as the maximum value of type 'size_t', if the system doesn't define
it. */
#undef SIZE_MAX
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
automatically deduced at runtime.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
#undef STACK_DIRECTION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
#endif
/* Define to `long int' if <sys/types.h> does not define. */
#undef off_t
/* Define as the type of the result of subtracting two pointers, if the system
doesn't define it. */
#undef ptrdiff_t
/* Define to empty if the C compiler doesn't support this keyword. */
#undef signed
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define to unsigned long or unsigned long long if <stdint.h> and
<inttypes.h> don't define. */
#undef uintmax_t

View File

@@ -0,0 +1,480 @@
/*
* Copyright (C) 2003 Daniel Heck
*
* 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 D_ENGINE_HH
#define D_ENGINE_HH
#include "ecl_geom.hh"
#include "ecl_array2.hh"
#include "ecl_alist.hh"
#include "ecl_fwd.hh"
namespace display
{
/* -------------------- DisplayEngine -------------------- */
class DisplayEngine {
public:
DisplayEngine (int tilew=32, int tileh=32);
~DisplayEngine();
/* ---------- Class configuration ---------- */
void add_layer (DisplayLayer *l);
void set_screen_area (const ecl::Rect & r);
void set_tilesize (int w, int h);
int get_tilew () const { return m_tilew; }
int get_tileh () const { return m_tileh; }
int get_width() const { return m_width; }
int get_height() const { return m_height; }
const ecl::Rect &get_area() const { return m_area; }
/* ---------- Scrolling / page flipping ---------- */
void set_offset (const ecl::V2 &off);
void move_offset (const ecl::V2 &off);
ecl::V2 get_offset () const { return m_offset; }
/* ---------- Game-related stuff ---------- */
void new_world (int w, int h);
void tick (double dtime);
/* ---------- Coordinate conversion ---------- */
void world_to_screen (const ecl::V2 & pos, int *x, int *y);
WorldArea screen_to_world (const ScreenArea &a);
ScreenArea world_to_screen (const WorldArea &a);
/* "Video" coordinates are like screen coordinates, except the
origin coincides with the world origin, not the current
scrolling position. */
void world_to_video (const ecl::V2 &pos, int *x, int *y);
void video_to_screen (int x, int y, int *xx, int *yy);
void video_to_world (const ecl::Rect &r, ecl::Rect &s);
V2 to_world (const V2 &pos);
/* ---------- Screen upates ---------- */
void mark_redraw_screen();
void mark_redraw_area (const WorldArea &wa, int delay=0);
void redraw_screen_area (const ScreenArea &a);
void redraw_world_area (const WorldArea &a);
void update_screen();
void draw_all (ecl::GC &gc);
void update_offset();
private:
void update_layer (DisplayLayer *l, WorldArea wa);
/* ---------- Variables ---------- */
std::vector<DisplayLayer *> m_layers;
int m_tilew, m_tileh;
// Offset of screen
ecl::V2 m_offset; // Offset in world units
ecl::V2 m_new_offset; // New offset in world units
int m_screenoffset[2]; // Offset in screen units
// Screen area occupied by level display
ecl::Rect m_area;
// Width and height of the world in tiles
int m_width, m_height;
ecl::Array2<char> m_redrawp;
};
/* -------------------- DisplayLayer -------------------- */
class DisplayLayer {
public:
DisplayLayer() {}
virtual ~DisplayLayer() {}
/* ---------- Class configuration ---------- */
void set_engine (DisplayEngine *e) { m_engine = e; }
DisplayEngine *get_engine() const { return m_engine; }
/* ---------- DisplayLayer interface ---------- */
virtual void prepare_draw (const WorldArea &) {}
virtual void draw (ecl::GC &gc, const WorldArea &a, int x, int y) = 0;
virtual void draw_onepass (ecl::GC &/*gc*/) {}
virtual void tick (double /*dtime*/) {}
virtual void new_world (int /*w*/, int /*h*/) {}
// Functions.
void mark_redraw_area (const ecl::Rect &r)
{
get_engine()->mark_redraw_area(r);
}
private:
DisplayEngine *m_engine;
};
/* -------------------- ModelLayer -------------------- */
/*! The base class for all layers that contains Models. */
class ModelLayer : public DisplayLayer {
public:
ModelLayer() {}
// DisplayLayer interface
void tick (double dtime);
void new_world (int, int);
// Member functions
void activate (Model *m);
void deactivate (Model *m);
void maybe_redraw_model(Model *m, bool immediately=false);
virtual int redraw_size () const { return 2; }
private:
// Variables
ModelList m_active_models;
ModelList m_active_models_new;
};
/* -------------------- DL_Grid -------------------- */
/*! Layer for grid-aligned models (stones, floor tiles, items). */
class DL_Grid : public ModelLayer {
public:
DL_Grid(int redrawsize = 1);
~DL_Grid();
void set_model (int x, int y, Model *m);
Model *get_model (int x, int y);
Model *yield_model (int x, int y);
private:
// DL_Grid interface.
void mark_redraw (int x, int y);
// DisplayLayer interface.
void new_world (int w, int h);
void draw (ecl::GC &gc, const WorldArea &a, int x, int y);
// ModelLayer interface
virtual int redraw_size () const { return m_redrawsize; }
// Variables.
typedef ecl::Array2<Model*> ModelArray;
ModelArray m_models;
int m_redrawsize;
};
/* -------------------- Sprites -------------------- */
class Sprite : public ecl::Nocopy {
public:
Model *model;
V2 pos;
int screenpos[2];
SpriteLayer layer;
bool visible;
bool mayNeedRedraw;
Sprite (const V2 & p, SpriteLayer l, Model *m)
: model(m), pos(p), layer(l), visible(true), mayNeedRedraw(false)
{
screenpos[0] = screenpos[1] = 0;
}
~Sprite() { delete model; }
};
typedef std::vector<Sprite*> SpriteList;
class DL_Sprites : public ModelLayer {
public:
DL_Sprites();
~DL_Sprites();
/* ---------- DisplayLayer interface ---------- */
void draw (ecl::GC &gc, const WorldArea &a, int x, int y);
void draw_onepass (ecl::GC &gc);
void new_world (int, int);
/* ---------- Member functions ---------- */
SpriteId add_sprite (Sprite *sprite);
void kill_sprite (SpriteId id);
void move_sprite (SpriteId, const ecl::V2& newpos);
void replace_sprite (SpriteId id, Model *m);
void redraw_sprite_region (SpriteId id);
void draw_sprites (bool shades, ecl::GC &gc);
Model *get_model (SpriteId id) { return sprites[id]->model; }
void set_maxsprites (unsigned m) { maxsprites = m; }
Sprite *get_sprite(SpriteId id);
static const SpriteId MAGIC_SPRITEID = 1000000;
SpriteList sprites;
private:
// ModelLayer interface
virtual void tick (double /*dtime*/);
// Variables.
unsigned numsprites; // Current number of sprites
unsigned maxsprites; // Maximum number of sprites
};
/* -------------------- Shadows -------------------- */
struct StoneShadowCache;
class DL_Shadows : public DisplayLayer {
public:
DL_Shadows(DL_Grid *grid, DL_Sprites *sprites);
~DL_Shadows();
void new_world(int w, int h);
void draw (ecl::GC &gc, int xpos, int ypos, int x, int y);
void draw (ecl::GC &gc, const WorldArea &a, int x, int y);
private:
/* ---------- Private functions ---------- */
void shadow_blit (ecl::Surface *scr, int x, int y,
ecl::Surface *shadows, ecl::Rect r);
bool has_actor (int x, int y);
virtual void prepare_draw (const WorldArea &);
Model * get_shadow_model(int x, int y);
/* ---------- Variables ---------- */
DL_Grid *m_grid; // Stone models
DL_Sprites *m_sprites; // Sprite models
StoneShadowCache *m_cache;
Uint32 shadow_ckey; // Color key
ecl::Surface *buffer;
ecl::Array2<bool> m_hasactor;
};
/* -------------------- Lines -------------------- */
struct Line {
V2 start,end;
V2 oldstart, oldend;
Line(const V2 &s, const V2 &e) :start(s), end(e) {}
Line() {}
};
typedef ecl::AssocList<unsigned, Line> LineMap;
class DL_Lines : public DisplayLayer {
public:
DL_Lines() : m_id(1)
{
}
void draw (ecl::GC &/*gc*/, const WorldArea &/*a*/, int /*x*/, int /*y*/)
{}
void draw_onepass (ecl::GC &gc);
RubberHandle add_line (const V2 &p1, const V2 &p2);
void set_startpoint (unsigned id, const V2 &p1);
void set_endpoint (unsigned id, const V2 &p2);
void kill_line (unsigned id);
private:
// Private methods.
void mark_redraw_line (const Line &r);
// Variables.
unsigned m_id;
LineMap m_rubbers;
};
/* -------------------- CommonDisplay -------------------- */
/*! Parts of the display engine that are common to the game and
the editor. */
class CommonDisplay {
public:
CommonDisplay (const ScreenArea &a = ScreenArea (0, 0, 10, 10));
~CommonDisplay();
Model *set_model (const GridLoc &l, Model *m);
Model *get_model (const GridLoc &l);
Model *yield_model (const GridLoc &l);
void set_floor (int x, int y, Model *m);
void set_item (int x, int y, Model *m);
void set_stone (int x, int y, Model *m);
DisplayEngine *get_engine() const { return m_engine; }
SpriteHandle add_effect (const V2& pos, Model *m);
SpriteHandle add_sprite (const V2 &pos, Model *m);
RubberHandle add_line (V2 p1, V2 p2);
void new_world (int w, int h);
void redraw();
protected:
DL_Grid *floor_layer;
DL_Grid *item_layer;
DL_Grid *stone_layer;
DL_Sprites *effects_layer;
DL_Lines *line_layer;
DL_Sprites *sprite_layer;
DL_Shadows *shadow_layer;
private:
DisplayEngine *m_engine;
};
/* -------------------- Scrolling -------------------- */
class Follower {
public:
Follower (DisplayEngine *e);
virtual ~Follower() {}
virtual void tick(double dtime, const ecl::V2 &point) = 0;
virtual void center(const ecl::V2 &point);
void set_boundary (double b) { m_boundary = b; }
protected:
DisplayEngine *get_engine() const { return m_engine; }
bool set_offset (V2 offs);
double get_hoff() const;
double get_voff() const;
ecl::V2 get_scrollpos(const ecl::V2 &point);
double m_boundary;
private:
DisplayEngine *m_engine;
};
/*! Follows a sprite by flipping to the next screen as soon as the
sprite reaches the border of the current screen. */
class Follower_Screen : public Follower {
public:
Follower_Screen(DisplayEngine *e);
void tick(double dtime, const ecl::V2 &point);
};
/*! Follows a sprite by softly scrolling the visible area of the
screen as soon as the sprite reaches the border of the current
screen. */
class Follower_Scrolling : public Follower {
public:
Follower_Scrolling (DisplayEngine *e, bool screenwise_);
void tick(double dtime, const ecl::V2 &point);
void center(const ecl::V2 &point);
private:
bool currently_scrolling;
V2 curpos, destpos;
V2 dir;
double scrollspeed;
double resttime;
bool screenwise;
};
class Follower_Smooth : public Follower {
public:
Follower_Smooth (DisplayEngine *e);
void tick (double time, const ecl::V2 &point);
void center (const ecl::V2 &point);
virtual void set_boundary (double b) {}
ecl::V2 calc_offset (const ecl::V2 &point);
};
/* -------------------- GameDisplay -------------------- */
class GameDisplay : public CommonDisplay {
public:
GameDisplay (const ScreenArea &gamearea,
const ScreenArea &inventoryarea);
~GameDisplay();
StatusBar * get_status_bar() const;
void tick(double dtime);
void new_world (int w, int h);
void resize_game_area (int w, int h);
/* ---------- Scrolling ---------- */
void set_follow_mode (FollowMode m);
void follow_center();
void set_follow_sprite(SpriteId id);
void set_reference_point (const ecl::V2 &point);
void set_scroll_boundary (double d);
// current screen coordinates of reference point
void get_reference_point_coordinates(int *x, int *y);
/* ---------- Screen updates ---------- */
void redraw (ecl::Screen *scr);
void redraw_all (ecl::Screen *scr);
void draw_all (ecl::GC &gc);
private:
void set_follower (Follower *f);
void draw_borders (ecl::GC &gc);
/* ---------- Variables ---------- */
Uint32 last_frame_time;
bool redraw_everything;
StatusBarImpl *status_bar;
V2 m_reference_point;
Follower *m_follower;
ScreenArea inventoryarea;
};
class ModelHandle {
public:
ModelHandle ();
};
}
#endif

View File

@@ -0,0 +1,623 @@
/*
* Copyright (C) 2002,2003,2005 Daniel Heck
*
* 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 "display_internal.hh"
#include "d_models.hh"
#include "lua.hh"
#include "options.hh"
#include "d_engine.hh"
#include "video.hh"
#include "main.hh"
#include "nls.hh"
#include "gui/ErrorMenu.hh"
#include "SDL_image.h"
#include <cstdio>
#include <iostream>
using namespace enigma;
using namespace display;
using namespace std;
using namespace ecl;
#ifndef CXXLUA
extern "C" {
#include "lualib.h"
#include "tolua++.h"
}
#else
#include "lualib.h"
#include "tolua++.h"
#endif
#include "lua-global.hh"
#include "lua-display.hh"
#include "lua-enigma.hh"
#include "lua-ecl.hh"
/* -------------------- Types -------------------- */
namespace
{
class SurfaceCache_Alpha : public PtrCache<Surface>{
public:
Surface *acquire(const std::string &name);
};
class SurfaceCache : public PtrCache<Surface>{
public:
Surface *acquire(const std::string &name);
};
class ModelManager {
public:
ModelManager();
~ModelManager();
void define (const std::string name, Model *m);
/* Create new model of type `name'. Returns 0 if no such
model exists. */
Model *create (const std::string &name);
/* Remove model definition for `name'. */
void remove (const std::string &name);
bool has_model (const std::string &name) const;
size_t num_templates() const;
private:
// Variables
typedef ecl::Dict<Model*> ModelMap;
ModelMap m_templates;
};
}
/* -------------------- SurfaceCache -------------------- */
Surface *SurfaceCache_Alpha::acquire(const std::string &name)
{
string filename;
if (app.resourceFS->findImageFile (name + ".png", filename))
return ecl::LoadImage(filename.c_str());
else
return 0;
}
Surface *SurfaceCache::acquire(const std::string &name)
{
string filename;
if (app.resourceFS->findImageFile (name + ".png", filename)) {
SDL_Surface *s = IMG_Load(filename.c_str());
if (s) {
SDL_Surface *img = 0;
if (s->flags & SDL_SRCALPHA) {
img = SDL_DisplayFormatAlpha(s);
} else {
SDL_SetColorKey(s, SDL_SRCCOLORKEY, //|SDL_RLEACCEL,
SDL_MapRGB(s->format, 255,0,255));
img = SDL_DisplayFormat(s);
}
if (img) {
SDL_FreeSurface(s);
return Surface::make_surface(img);
}
return Surface::make_surface(s);
}
}
return 0;
}
/* -------------------- ModelManager -------------------- */
ModelManager::ModelManager()
: m_templates (1069)
{}
ModelManager::~ModelManager() {
delete_map (m_templates.begin(), m_templates.end());
}
void ModelManager::define (const std::string name, Model *m) {
m_templates.insert (name, m);
}
Model * ModelManager::create (const std::string &name) {
ModelMap::iterator i = m_templates.find(name);
if (i != m_templates.end())
return i->second->clone();
else
return 0;
}
void ModelManager::remove (const std::string &name)
{
ModelMap::iterator i = m_templates.find(name);
if (i != m_templates.end()) {
delete i->second;
m_templates.remove (name);
}
}
bool ModelManager::has_model (const std::string &name) const {
return m_templates.has_key (name);
}
size_t ModelManager::num_templates() const {
return m_templates.size();
}
/* -------------------- Variables -------------------- */
namespace
{
SurfaceCache surface_cache;
SurfaceCache_Alpha surface_cache_alpha;
ModelManager *modelmgr = 0;
vector<Surface *> image_pile;
string anim_templ_name;
Anim2d *anim_templ = 0;
}
/* -------------------- Functions -------------------- */
void display::InitModels()
{
modelmgr = new ModelManager;
lua_State *L = lua_open();
luaL_openlibs(L);
lua_register (L, "FindDataFile", lua::FindDataFile);
tolua_open(L);
tolua_global_open(L);
tolua_enigma_open(L);
tolua_display_open(L);
tolua_px_open(L);
if (lua::DoSysFile(L, "compat.lua") != lua::NO_LUAERROR) {
std::string message = ecl::strf("Error loading 'compat.lua'\nError: '%s'\n",
lua::LastError(L).c_str());
fprintf(stderr, message.c_str());
gui::ErrorMenu m(message +
_("\n\nThis error may cause the application to behave strange!")
, N_("Continue"));
m.manage();
}
string fname;
const video::VMInfo *vminfo = video::GetInfo();
fname = app.systemFS->findFile (vminfo->initscript);
if (lua::DoSysFile(L, vminfo->initscript) != lua::NO_LUAERROR) {
std::string message = ecl::strf("Error loading '%s'\nError: '%s'\n",
fname.c_str(), lua::LastError(L).c_str());
fprintf(stderr, message.c_str());
gui::ErrorMenu m(message +
_("\n\nThis error may cause the application to behave strange!")
, N_("Continue"));
m.manage();
}
enigma::Log << "# models: " << modelmgr->num_templates() << endl;
surface_cache_alpha.clear();
lua_close(L);
}
void display::ShutdownModels()
{
delete modelmgr;
surface_cache.clear();
delete_sequence (image_pile.begin(), image_pile.end());
image_pile.clear();
anim_templ_name = "";
anim_templ = 0;
}
Surface *display::CropSurface (const Surface *s, Rect r) {
return ecl::Grab(s, r);
}
/* Register a new model template `m' under the name `name'. */
void display::DefineModel (const char *name, Model *m)
{
if (modelmgr->has_model (name)) {
enigma::Log << "Redefining model '" << name << "'\n";
modelmgr->remove (name);
}
modelmgr->define (name, m);
}
Model * display::MakeModel (const string &name)
{
if (Model *m = modelmgr->create (name))
return m;
else {
enigma::Log << "Unknown model " << name << endl;
return modelmgr->create ("dummy");
}
}
int display::DefineImage(const char *name, const char *fname,
int xoff, int yoff, int padding)
{
ecl::Surface *sfc = surface_cache.get(fname);
if (!sfc)
return 1;
ecl::Rect r = sfc->size();
r.x += padding; r.y += padding;
r.w -= 2*padding; r.h -= 2*padding;
DefineModel(name, new ImageModel(sfc, r, xoff+padding, yoff+padding));
return 0;
}
void display::DefineImageModel (const char *name, ecl::Surface *s)
{
DefineModel (name, new ImageModel(s, 0, 0));
}
int display::DefineSubImage(const char *name, const char *fname,
int xoff, int yoff, ecl::Rect subrect)
{
ecl::Surface *sfc = surface_cache.get(fname);
if (!sfc)
return 1;
DefineModel(name, new ImageModel(sfc, subrect, xoff, yoff));
return 0;
}
void display::DefineRandModel(const char *name, int n, char **names)
{
RandomModel *m = new RandomModel();
for (int i=0; i<n; i++)
m->add_model(names[i]);
DefineModel(name, m);
}
void display::DefineShadedModel (const char *name, const char *model, const char *shadow)
{
DefineModel(name, new ShadowModel(MakeModel(model), MakeModel(shadow)));
}
/* Create an image by overlaying several other images. The first entry in
`images' is the name of the background image, the following images are
drawn on top of it. */
void display::DefineOverlayImage (const char *name, int n,
char **images)
{
Surface *sfc = Duplicate(surface_cache.get(images[0]));
if (sfc) {
GC gc(sfc);
for (int i=1; i<n; i++)
blit (gc, 0,0, surface_cache_alpha.get(images[i]));
DefineModel(name, new ImageModel(sfc, 0,0));
image_pile.push_back(sfc); // make sure it gets destructed
}
}
void display::DefineComposite (const char *name, const char *bgname, const char *fgname)
{
DefineModel(name, new CompositeModel(MakeModel(bgname), MakeModel(fgname)));
}
void display::DefineAnim (const char *name, bool loop_p) {
anim_templ = new Anim2d(loop_p);
DefineModel(name, anim_templ);
anim_templ_name = name;
}
void display::AddFrame (const char *name, const char *model, double time)
{
if (anim_templ_name != name)
fprintf(stderr, "AddFrame: Cannot add frames to completed animations.");
else
anim_templ->add_frame(MakeModel(model), time / 1000.0);
}
void display::DefineAlias (const char *name, const char *othername)
{
DefineModel(name, new AliasModel(othername));
}
/* -------------------- Model -------------------- */
void Model::get_extension (ecl::Rect &r)
{}
/* -------------------- Image -------------------- */
Image::Image(ecl::Surface *sfc)
: surface(sfc), rect(surface->size()), refcount(1)
{}
Image::Image(ecl::Surface *sfc, const ecl::Rect &r)
: surface(sfc), rect(r), refcount(1)
{}
void display::incref(Image *i) {
++i->refcount;
}
void display::decref (Image *i) {
if (-- i->refcount == 0) {
delete i;
}
}
void display::draw_image (Image *i, ecl::GC &gc, int x, int y)
{
blit(gc, x, y, i->surface, i->rect);
}
/* -------------------- ImageModel -------------------- */
ImageModel::ImageModel (Image *i, int xo, int yo)
: image(i), xoff(xo), yoff(yo)
{
assert(image);
incref(image);
}
ImageModel::ImageModel(Surface *s, int xo, int yo)
: image(new Image(s)), xoff(xo), yoff(yo)
{}
ImageModel::ImageModel(Surface *s, const ecl::Rect &r, int xo, int yo)
: image(new Image(s, r)), xoff(xo), yoff(yo)
{}
ImageModel::~ImageModel() {
decref(image);
}
void ImageModel::draw(ecl::GC &gc, int x, int y) {
draw_image (image, gc, x+xoff, y+yoff);
}
Model *ImageModel::clone() {
return new ImageModel(image, xoff, yoff);
}
void ImageModel::get_extension (ecl::Rect &r) {
r.x = xoff;
r.y = yoff;
r.w = image->rect.w;
r.h = image->rect.h;
}
/* -------------------- ShadowModel -------------------- */
ShadowModel::ShadowModel (Model *m, Model *sh) {
model=m; shade=sh;
}
ShadowModel::~ShadowModel() {
delete model; delete shade;
}
void ShadowModel::expose (ModelLayer *ml, int vx, int vy) {
model->expose(ml, vx, vy);
shade->expose(ml, vx, vy);
}
void ShadowModel::remove (ModelLayer *ml) {
shade->remove(ml);
model->remove(ml);
}
void ShadowModel::set_callback(ModelCallback *cb) {
model->set_callback(cb);
}
void ShadowModel::reverse() {
model->reverse();
shade->reverse();
}
void ShadowModel::restart() {
model->restart();
shade->restart();
}
void ShadowModel::draw(ecl::GC &gc, int x, int y) {
model->draw(gc,x,y);
}
void ShadowModel::draw_shadow(ecl::GC &gc, int x, int y) {
shade->draw(gc,x,y);
}
Model *ShadowModel::get_shadow() const {
return shade;
}
Model *ShadowModel::clone() {
return new ShadowModel(model->clone(), shade->clone());
}
void ShadowModel::get_extension (ecl::Rect &r) {
ecl::Rect r1, r2;
model->get_extension (r1);
shade->get_extension (r2);
r = boundingbox (r1, r2);
}
/* -------------------- RandomModel -------------------- */
Model * RandomModel::clone() {
if (!modelnames.empty()) {
int r = enigma::IntegerRand(0, modelnames.size()-1);
return MakeModel(modelnames[r]);
} else {
fprintf(stderr, "display_2d.cc: empty RandomModel\n");
return 0;
}
}
/* -------------------- AliasModel -------------------- */
Model * AliasModel::clone() {
return MakeModel(name);
}
/* -------------------- Anim2d -------------------- */
Anim2d::Anim2d(bool loop)
: rep(new AnimRep(loop))
{}
Anim2d::Anim2d (AnimRep *r)
: rep(r), curframe(0), frametime(0),
finishedp (false),
changedp (false),
reversep (false),
videox (0), videoy (0),
callback(0)
{
rep->refcount++;
frametime = 0; // enigma::DoubleRand (0, 0.02);
}
Anim2d::~Anim2d() {
if (--rep->refcount == 0)
delete rep;
}
void Anim2d::restart () {
finishedp = false; frametime = 0; curframe = 0; changedp = true;
}
void Anim2d::add_frame(Model *m, double duration) {
rep->frames.push_back(new AnimFrame(m, duration));
}
void Anim2d::draw(ecl::GC &gc, int x, int y)
{
if (!finishedp) {
AnimFrame *f =rep->frames[curframe];
f->model->draw(gc,x,y);
changedp = false;
}
}
void Anim2d::draw_shadow (ecl::GC &gc, int x, int y)
{
if (!finishedp) {
AnimFrame *f =rep->frames[curframe];
f->model->draw_shadow(gc,x,y);
}
}
void Anim2d::expose (ModelLayer *ml, int vx, int vy) {
ml->activate (this);
videox = vx;
videoy = vy;
}
void Anim2d::remove (ModelLayer *ml) {
ml->deactivate (this);
}
bool Anim2d::has_changed (Rect &r) {
bool retval = changedp;
if (changedp) {
get_extension (r);
r.x += videox;
r.y += videoy;
}
return retval;
}
void Anim2d::move (int newx, int newy) {
videox = newx;
videoy = newy;
}
void Anim2d::get_extension (ecl::Rect &r) {
AnimFrame *f =rep->frames[curframe];
f->model->get_extension (r);
}
void Anim2d::tick (double dtime)
{
assert(curframe < rep->frames.size());
frametime += dtime;
double framedur = rep->frames[curframe]->duration;
if (frametime >= framedur) {
frametime -= framedur;
changedp = true;
if (reversep) {
if (curframe >= 1)
curframe--;
else if (rep->loop)
curframe = rep->frames.size()-1;
else
finishedp = true;
}
else {
if (curframe+1 < rep->frames.size())
curframe++;
else if (rep->loop)
curframe = 0;
else
finishedp = true;
}
if (finishedp && callback!=0)
callback->animcb();
}
}
/* -------------------- Functions -------------------- */
namespace display
{
Surface *GetSurface (const std::string &filename)
{
return surface_cache.get(filename);
}
void RegisterImage (const std::string &name, Image *img)
{
}
Image *LookupImage (const std::string &name)
{
return 0;
}
}

View File

@@ -0,0 +1,241 @@
/*
* Copyright (C) 2002,2003 Daniel Heck
*
* 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 D_MODELS_HH
#define D_MODELS_HH
#include "ecl_fwd.hh"
#include <vector>
#include <string>
#include <cassert>
namespace display
{
using std::string;
using std::vector;
using ecl::Surface;
/* -------------------- Image -------------------- */
struct Image {
// Variables.
ecl::Surface *surface;
ecl::Rect rect; // location of image inside surface
int refcount; // reference count, initialized to 1
// Constructors.
Image(ecl::Surface *sfc);
Image(ecl::Surface *sfc, const ecl::Rect &r);
};
void incref (Image *i);
void decref (Image *i);
void draw_image (Image *i, ecl::GC &gc, int x, int y);
/* -------------------- ImageModel -------------------- */
class ImageModel : public Model {
Image* image;
int xoff, yoff; // relative origin of the image
public:
// Constructors
ImageModel (Image *i, int xo, int yo);
ImageModel (Surface *s, int xo, int yo);
ImageModel (Surface *s, const ecl::Rect &r, int xo, int yo);
~ImageModel();
// Model interface
void draw(ecl::GC &gc, int x, int y);
Model *clone();
void get_extension (ecl::Rect &r);
Image *get_image() { return image; }
};
/* -------------------- ShadowModel -------------------- */
class ShadowModel : public Model {
public:
ShadowModel(Model *m, Model *sh);
~ShadowModel();
// Model interface
void expose (ModelLayer *ml, int vx, int vy);
void remove (ModelLayer *ml);
void set_callback(ModelCallback *cb);
void reverse();
void restart();
void draw (ecl::GC &gc, int x, int y);
void draw_shadow (ecl::GC &gc, int x, int y);
Model *get_shadow() const;
Model *clone();
void get_extension (ecl::Rect &r);
private:
Model *model, *shade;
};
/* -------------------- CompositeModel -------------------- */
class CompositeModel : public Model {
Model *bg, *fg;
public:
CompositeModel(Model *b, Model *f) : bg(b), fg(f) {}
~CompositeModel() {
delete bg; delete fg;
}
// Animation interface
void set_callback(ModelCallback *cb) {
// bg->set_callback(cb);
fg->set_callback(cb);
}
void reverse() {
// bg->reverse();
fg->reverse();
}
void restart() {
fg->restart();
}
// Model interface
Model *get_shadow() const { return bg->get_shadow(); }
virtual void expose (ModelLayer *ml, int vx, int vy) {
fg->expose (ml, vx, vy);
// bg->expose (ml, vx, vy);
}
virtual void remove (ModelLayer *ml) {
fg->remove (ml);
// bg->remove (ml);
}
void draw(ecl::GC &gc, int x, int y) {
bg->draw(gc,x,y);
fg->draw(gc,x,y);
}
void draw_shadow(ecl::GC &gc, int x, int y) {
bg->draw_shadow(gc,x,y);
}
Model *clone() {
return new CompositeModel(bg->clone(), fg->clone());
}
void get_extension (ecl::Rect &r) {
fg->get_extension (r);
// ecl::Rect r1, r2;
// bg->get_extension (r1);
// fg->get_extension (r2);
// r = boundingbox (r1, r2);
}
};
/* -------------------- RandomModel -------------------- */
/* Creates new models randomly from a set of template models. */
class RandomModel : public Model {
std::vector<std::string> modelnames;
public:
void add_model(const std::string &name) {modelnames.push_back(name);}
Model *clone();
};
/* -------------------- AliasModel -------------------- */
class AliasModel : public Model {
string name;
public:
AliasModel(const string &modelname) : name(modelname) {}
Model *clone();
};
/* -------------------- Animations -------------------- */
struct AnimFrame : public ecl::Nocopy {
// Variables
Model *model;
double duration;
// Constructor and Destructor
AnimFrame(Model *m, double dur)
: model(m), duration(dur)
{}
~AnimFrame() { delete model; }
};
struct AnimRep {
// Variables
vector<AnimFrame*> frames;
bool loop;
int refcount;
// Constructor and Destructor
AnimRep(bool l) : loop(l), refcount(1) {}
~AnimRep() {
delete_sequence(frames.begin(), frames.end());
}
};
class Anim2d : public Model, public ecl::Nocopy {
public:
Anim2d (bool loop);
~Anim2d();
void set_callback(ModelCallback *cb) { callback = cb; }
void add_frame(Model *m, double duration);
/* ---------- Model interface ---------- */
void draw(ecl::GC &gc, int x, int y);
void draw_shadow(ecl::GC &gc, int x, int y);
Model *clone() { return new Anim2d(rep); }
void reverse() { reversep = !reversep; }
void restart ();
void expose (ModelLayer *ml, int vx, int vy);
void remove (ModelLayer *ml);
void tick(double dtime);
bool has_changed(ecl::Rect &changed_region);
bool is_garbage() const { return finishedp; }
void move (int newx, int newy);
void get_extension (ecl::Rect &r);
private:
Anim2d(AnimRep *r);
/* ---------- Variables ---------- */
AnimRep *rep;
unsigned curframe; // Current frame number
double frametime; // Elapsed time since frame was activated
bool finishedp; // Animation has finished
bool changedp; // Model state has changed since last redraw
bool reversep; // Play the animation in reverse direction
int videox, videoy; // Video coordinates of sprite
ModelCallback *callback;
};
ecl::Surface *GetSurface (const std::string &filename);
ecl::Surface *CropSurface (const ecl::Surface *s, ecl::Rect r);
void DefineModel (const char *name, Model *m);
void DefineImageModel (const char *name, ecl::Surface *s);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C) 2002,2003,2004,2005 Daniel Heck
*
* 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 DISPLAY_HH_INCLUDED
#define DISPLAY_HH_INCLUDED
#include "enigma.hh"
#include "ecl.hh"
//----------------------------------------
// Definition of models
//----------------------------------------
namespace display
{
class DisplayLayer;
class ModelLayer;
/*! Animations can invoke a callback of this type on completion.
Note that you may not delete or replace models other than the
one that induced the callback from inside the callback--use a
timer or a flag to do this. */
class ModelCallback {
public:
virtual ~ModelCallback() {}
virtual void animcb() = 0;
};
class Animation {
public:
virtual ~Animation() {}
virtual void set_callback (ModelCallback *) {}
virtual void reverse() {}
virtual void restart() {}
virtual bool is_garbage() const { return false; }
virtual void tick(double /*dtime*/) {}
virtual bool has_changed(ecl::Rect &/*changed_region*/) { return false; }
};
class Model : public Animation {
public:
virtual void draw(ecl::GC &/*gc*/, int /*x*/, int /*y*/) {}
virtual void draw_shadow(ecl::GC &/*gc*/, int /*x*/, int /*y*/) {}
virtual Model *get_shadow() const { return 0; }
virtual void expose (ModelLayer * /*ml*/, int videox, int videoy) {}
virtual void remove (ModelLayer * /*ml*/) {}
virtual Model *clone() = 0;
virtual void get_extension (ecl::Rect &r);
};
/* -------------------- Functions -------------------- */
void InitModels();
void ShutdownModels();
Model * MakeModel (const std::string &name);
int DefineImage (const char *name, const char *fname,
int xoff, int yoff, int padding);
int DefineSubImage (const char *name, const char *fname,
int xoff, int yoff, ecl::Rect r);
void DefineRandModel (const char *name, int n, char **names);
void DefineShadedModel (const char *name, const char *model, const char *shade);
void DefineOverlayImage (const char *name, int n, char **images);
void DefineComposite (const char *name, const char *bgname, const char *fgname);
void DefineAnim (const char *name, bool loop_p);
void AddFrame (const char *name, const char *model, double time);
void DefineAlias (const char *name, const char *othername);
}
//----------------------------------------
// Models on the grid
//----------------------------------------
namespace display
{
using enigma::GridPos;
using enigma::GridLayer;
using enigma::GridLoc;
Model* SetModel (const GridLoc & l, const std::string &modelname);
Model* SetModel (const GridLoc & l, Model *m);
void KillModel (const GridLoc & l);
Model* GetModel (const GridLoc & l);
Model* YieldModel (const GridLoc & l);
}
/* -------------------- Scrolling -------------------- */
namespace display
{
enum FollowMode {
FOLLOW_NONE = 0, // Don't follow any sprite
FOLLOW_SCROLLING = 1, // Scroll the screen
FOLLOW_SCREEN = 2, // Flip the screen region
FOLLOW_SCREENSCROLLING = 3, // Scroll to the next screen
FOLLOW_SMOOTH = 4, // Follow pixel by pixel
};
void SetFollowMode (FollowMode m);
void SetScrollBoundary (double boundary);
void SetReferencePoint (const ecl::V2 &point);
void GetReferencePointCoordinates(int *x, int *y);
void FocusReferencePoint();
}
/* -------------------- Sprites -------------------- */
namespace display
{
enum SpriteLayer {
SPRITE_ACTOR, SPRITE_EFFECT, SPRITE_DEBRIS
};
typedef unsigned int SpriteId;
class DL_Sprites;
class SpriteHandle {
DL_Sprites *layer;
unsigned id;
public:
SpriteHandle (DL_Sprites *l, unsigned spriteid);
SpriteHandle();
void kill();
void move (const ecl::V2 &newpos) const;
void replace_model (Model *m) const;
Model *get_model () const;
void set_callback (ModelCallback *cb) const;
void hide() const;
void show() const;
};
/*! Add a new effect sprite. Sprites of this type are
automatically deleted once the animation has finished. */
SpriteHandle AddEffect (const ecl::V2 &pos, const char *modelname);
/*! Create a new sprite. If modelname==0, the sprite is
considered invisible. Sprites of this type are _never_
automatically deleted. */
SpriteHandle AddSprite (const ecl::V2 &pos, const char *modelname=0);
}
/* -------------------- Rubber bands -------------------- */
namespace display
{
class DL_Lines;
class RubberHandle {
public:
RubberHandle (DL_Lines *layer=0, unsigned id=0);
operator unsigned() { return id; }
void update_first (const ecl::V2 &p1);
void update_second (const ecl::V2 &p2);
void kill();
DL_Lines *line_layer;
unsigned id;
};
RubberHandle AddRubber (const ecl::V2 &p1, const ecl::V2 &p2);
}
/* -------------------- Status bar -------------------- */
namespace display
{
using enigma_player::Inventory;
class StatusBar {
public:
virtual ~StatusBar() {}
virtual void set_inventory (const std::vector<std::string> &modelnames) = 0;
virtual void show_text (const std::string &str,
bool scrolling,
double duration = -1) = 0;
virtual void hide_text() = 0;
virtual void show_move_counter (bool active) = 0;
virtual void show_odometer (bool active) = 0;
virtual void set_time (double time) = 0;
virtual void set_speed (double speed) = 0;
virtual void set_travelled_distance (double distance) = 0;
virtual void set_counter (int nummoves) = 0;
};
StatusBar *GetStatusBar();
#define STATUSBAR display::GetStatusBar()
/* -------------------- Interface to display engine -------------------- */
enum DisplayFlags
{
SHOW_FLOOR = 0x01,
SHOW_STONES = 0x02,
SHOW_ITEMS = 0x04,
SHOW_SHADES = 0x08,
SHOW_SPRITES = 0x10,
SHOW_TIME = 0x20,
SHOW_INVENTORY = 0x40,
SHOW_ALL = 0x7f
};
void ToggleFlag(DisplayFlags flag);
void Init();
void Shutdown();
void NewWorld (int w, int h);
void ResizeGameArea (int w, int h);
const ecl::Rect& GetGameArea ();
void DrawAll (ecl::GC &gc);
void RedrawAll (ecl::Screen *sfc);
void Redraw (ecl::Screen *sfc);
void Tick (double dtime);
}
#endif

View File

@@ -0,0 +1,96 @@
#ifndef DISPLAY_INTERNAL_HH
#define DISPLAY_INTERNAL_HH
#include "display.hh"
namespace display
{
using ecl::V2;
class DisplayLayer;
class StatusBarImpl;
class Model;
typedef ecl::Rect ScreenArea;
typedef ecl::Rect WorldArea;
typedef std::list<Model*> ModelList;
class Window {
public:
Window() {}
Window (const ScreenArea &area) : m_area(area)
{}
const ScreenArea &get_area() const { return m_area; }
private:
ScreenArea m_area;
};
class TextDisplay {
public:
TextDisplay(ecl::Font &f);
void set_text(const std::string &t, bool scrolling, double duration = -1);
void tick(double dtime);
bool has_changed() const { return changedp; }
bool has_finished() const { return finishedp; }
void draw(ecl::GC &gc, const ecl::Rect &r);
private:
ecl::Rect area;
std::string text;
bool changedp, finishedp;
bool pingpong;
bool showscroll;
double xoff;
double scrollspeed; // pixels per second
std::auto_ptr<ecl::Surface> textsurface;
ecl::Font &font;
double time, maxtime;
};
class StatusBarImpl : public StatusBar, public Window {
public:
StatusBarImpl (const ScreenArea &area);
~StatusBarImpl();
bool has_changed() const { return m_changedp; }
void redraw (ecl::GC &gc, const ScreenArea &r);
void tick (double dtime);
void new_world();
// StatusBar interface.
void set_time (double time);
void set_inventory (const std::vector<std::string> &modelnames);
void show_text (const std::string &str, bool scrolling, double duration);
void hide_text();
void show_move_counter (bool active);
void show_odometer (bool active);
void set_speed (double speed);
void set_travelled_distance (double distance);
void set_counter (int new_counter);
private:
ScreenArea m_itemarea;
std::vector<Model*> m_models;
bool m_changedp;
TextDisplay m_textview;
double m_leveltime;
bool m_showtime_p;
int m_counter;
bool m_showcounter_p;
bool m_showodometer_p;
bool m_interruptible; // Current text message may be interrupted
bool m_text_active;
};
}
#endif

View File

@@ -0,0 +1,469 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 "editor.hh"
#include "ecl_sdl.hh"
#include "ecl_video.hh" // set_color, line
#include "ecl_util.hh" // set_flags
#include "main.hh"
#include "world.hh"
#include "lua.hh"
#include "video.hh"
#include "gui/widgets.hh"
#include "display_internal.hh" // WorldArea
#include <iostream>
#include <cassert>
#include "display_internal.hh"
#include "d_engine.hh"
#ifndef CXXLUA
extern "C" {
#include "lualib.h"
#include "tolua++.h"
}
#else
#include "lualib.h"
#include "tolua++.h"
#endif
#include "lua-editor.hh"
using namespace std;
using namespace enigma;
using namespace editor;
using namespace ecl;
using display::ScreenArea;
using display::DisplayEngine;
using display::Model;
//using world::ObjectTraits;
using world::ItemID;
#include "editor_impl.hh"
/* -------------------- Editor state -------------------- */
EditorState::EditorState()
{
}
EditorState::~EditorState()
{
}
void EditorState::save( std::ostream & /*os*/ )
{
}
void EditorState::load( std::istream & /*is*/ )
{
}
/* -------------------- Editor display engine -------------------- */
EditorDisplay::EditorDisplay()
{
DisplayEngine *engine = get_engine();
engine->set_screen_area( video::GetInfo()->gamearea );
engine->add_layer( new DL_Editor );
}
void
EditorDisplay::redraw()
{
display::CommonDisplay::redraw();
}
/* -------------------- Editor tools -------------------- */
void
GridObjectTool::advance_in_group( int offset )
{
int newidx = m_objectidx + offset;
if( newidx >= 0 && newidx < (int)m_groups[ m_groupidx ].entries.size() ) {
m_objectidx = newidx;
}
}
void
GridObjectTool::advance_group( int offset )
{
int newidx = m_groupidx + offset;
if( newidx >= 0 && newidx < (int)m_groups.size() ) {
m_groupidx = newidx;
m_objectidx = 0;
}
}
void
GridObjectTool::on_mousebutton( const SDL_Event &e, V2 worldpos )
{
Editor *ed = Editor::get_instance();
string obj = current_object();
round_coordinates( &worldpos );
int x = (int)worldpos[ 0 ];
int y = (int)worldpos[ 1 ];
if( e.button.type == SDL_MOUSEBUTTONDOWN ) {
if( e.button.button == 1 ) {
// left mb -> set object
switch( m_layer ) {
case GRID_FLOOR: ed->set_floor( x, y, obj ); break;
case GRID_ITEMS: ed->set_item( x, y, obj ); break;
case GRID_STONES: ed->set_stone( x, y, obj ); break;
default: break;
}
}
else if( e.button.button == 3 ) {
// right mb
}
}
}
void
GridObjectTool::object_menu()
{
}
void
ActorTool::on_mousebutton( const SDL_Event &e, V2 worldpos )
{
Editor *ed = Editor::get_instance();
string obj = current_object();
if( e.button.type == SDL_MOUSEBUTTONDOWN ) {
if( e.button.button == 1 ) {
// left mb -> set object
ed->set_actor( worldpos[0], worldpos[1], obj );
}
else if( e.button.button == 3 ) {
// right mb
}
}
}
/* -------------------- The Editor -------------------- */
Editor *Editor::m_instance = 0;
Editor::Editor()
: m_editarea( 0,0,640,13*32 )
, m_iconarea( 0,13*32,640,64 )
, m_display()
, m_iconbar( m_iconarea, 2, 640/32 )
, m_quit_editor( false )
, m_cursor()
, m_lua( luaL_newstate() )
, m_editmode( MODE_FLOOR )
, m_tools()
{
luaL_openlibs(m_lua);
tolua_open( m_lua );
tolua_editor_open( m_lua );
}
Editor::~Editor()
{
lua_close( m_lua );
}
void
Editor::init()
{
delete_sequence( m_tools.begin(), m_tools.end() );
m_tools.clear();
m_tools.resize( MODE_COUNT, NULL );
m_tools[ MODE_FLOOR ] = m_floortool = new FloorTool;
m_tools[ MODE_ITEMS ] = m_itemtool = new ItemTool;
m_tools[ MODE_STONES ] = m_stonetool = new StoneTool;
m_tools[ MODE_ACTORS ] = m_actortool = new ActorTool;
new_world( 20, 13 );
// TODO - just printing a message is not enough - the app will crash on missing editor.lua
if (lua::DoSysFile(m_lua, "compat.lua") != lua::NO_LUAERROR) {
std::string message = "While processing 'compat.lua':\n" +lua::LastError(m_lua);
fprintf( stderr, message.c_str() );
}
if (lua::DoSysFile(m_lua, "editor.lua") != lua::NO_LUAERROR) {
std::string message = "Error loading 'editor.lua'\n" +lua::LastError(m_lua);
fprintf( stderr, message.c_str() );
}
}
void
Editor::run()
{
m_quit_editor = false;
while( !m_quit_editor ) {
video::HideMouse();
m_display.redraw();
video::ShowMouse();
video::GetScreen()->flush_updates();
SDL_Event e;
if( SDL_PollEvent( &e ) ) {
dispatch_event (e);
}
SDL_Delay( 10 );
}
}
void Editor::set_mode( EditMode m )
{
m_editmode = m;
update_cursor();
}
void Editor::update_cursor()
{
// set proper position
int mx;
int my;
SDL_GetMouseState( &mx, &my );
DisplayEngine *engine = m_display.get_engine();
V2 worldpos = engine->to_world( V2( mx, my ) );
current_tool()->round_coordinates( &worldpos );
m_cursor.move( worldpos );
// set proper face
set_cursor( current_tool()->current_object() );
}
Tool *Editor::current_tool()
{
if( Tool *tool = m_tools[ m_editmode ] ) {
return tool;
}
else {
fprintf( stderr, "undefined tool\n" );
assert( m_stonetool != NULL );
return m_stonetool;
}
}
void
Editor::set_floor( int x, int y, std::string const& name )
{
m_display.set_floor( x, y, display::MakeModel( name ) );
}
void
Editor::set_item( int x, int y, std::string const& name )
{
m_display.set_item( x, y, display::MakeModel( name ) );
}
void
Editor::set_stone( int x, int y, std::string const& name )
{
m_display.set_stone( x, y, display::MakeModel( name ) );
}
void
Editor::set_actor( double x, double y, std::string const& name )
{
m_display.add_sprite( V2( x, y ), display::MakeModel( name ) );
}
void
Editor::new_world( int w, int h )
{
m_display.new_world( w, h );
for( int x=0; x<w; ++x ) {
for( int y=0; y<h; ++y ) {
set_floor (x, y, "fl-normal");
}
}
m_cursor = m_display.add_effect( V2(), display::MakeModel( "it-hammer" ) );
}
void
Editor::set_cursor( const string &name )
{
m_cursor.replace_model( display::MakeModel( name ) );
}
void
Editor::scroll( double xoff, double yoff )
{
DisplayEngine *engine = m_display.get_engine();
V2 newoffset = engine->get_offset() + V2( xoff, yoff );
video::HideMouse();
engine->move_offset( newoffset );
video::ShowMouse();
}
void
Editor::scroll_abs( double x, double y )
{
DisplayEngine *engine = m_display.get_engine();
video::HideMouse();
engine->move_offset( V2( x, y ) );
video::ShowMouse();
}
bool
Editor::on_mousemotion( SDL_Event &e )
{
DisplayEngine *engine = m_display.get_engine();
V2 worldpos = engine->to_world( V2( e.motion.x, e.motion.y ) );
current_tool()->round_coordinates( &worldpos );
m_cursor.move( worldpos );
return true;
}
bool
Editor::on_mousebutton( SDL_Event &e )
{
DisplayEngine *engine = m_display.get_engine();
V2 worldpos = engine->to_world( V2( e.motion.x, e.motion.y ) );
if( Tool *tool = current_tool() ) {
tool->on_mousebutton( e, worldpos );
}
return true;
}
bool
Editor::on_keydown( SDL_Event &e )
{
bool ctrl_pressed = e.key.keysym.mod & KMOD_CTRL;
bool shift_pressed = e.key.keysym.mod & KMOD_SHIFT;
int hoff = 19;
int voff = 12;
Tool *tool = current_tool();
switch( e.key.keysym.sym ) {
case SDLK_ESCAPE:
m_quit_editor = true;
break;
case SDLK_LEFT: scroll( ctrl_pressed ? -hoff : -1, 0 ); break;
case SDLK_RIGHT: scroll( ctrl_pressed ? +hoff : +1, 0 ); break;
case SDLK_DOWN: scroll( 0, ctrl_pressed ? +voff : +1 ); break;
case SDLK_UP: scroll( 0, ctrl_pressed ? -voff : -1 ); break;
case SDLK_HOME: scroll_abs( 0, 0 ); break;
case SDLK_PAGEUP:
tool->advance_group( -1 );
update_cursor();
break;
case SDLK_PAGEDOWN:
tool->advance_group( +1 );
update_cursor();
break;
case SDLK_PLUS:
case SDLK_KP_PLUS:
tool->advance_in_group( +1 );
update_cursor();
break;
case SDLK_MINUS:
case SDLK_KP_MINUS:
tool->advance_in_group( -1 );
update_cursor();
break;
case SDLK_a:
if( shift_pressed )
;
set_mode( MODE_ACTORS );
break;
case SDLK_f:
if( shift_pressed )
;
set_mode( MODE_FLOOR );
break;
case SDLK_i:
if( shift_pressed )
;
set_mode( MODE_ITEMS );
break;
case SDLK_s:
set_mode( MODE_STONES );
if( shift_pressed ) {
tool = current_tool();
tool->object_menu();
}
break;
default:
return false;
}
return true;
}
void editor::DefineFloorGroup( char const* name, char const* descr,
int nentries, char ** entries )
{
Editor *ed = Editor::get_instance();
vector<string> entryvec(entries, entries+nentries);
ObjectGroup group(name, descr, entryvec);
ed->add_floor_group (group);
}
void editor::DefineItemGroup( const char *name, const char *descr,
int nentries, char **entries )
{
Editor *ed = Editor::get_instance();
vector<string> entryvec( entries, entries + nentries );
ObjectGroup group( name, descr, entryvec );
ed->add_item_group( group );
}
void editor::DefineStoneGroup( const char *name, const char *descr,
int nentries, char **entries )
{
Editor *ed = Editor::get_instance();
vector<string> entryvec( entries, entries+nentries );
ObjectGroup group( name, descr, entryvec );
ed->add_stone_group( group );
}
void editor::Run()
{
Editor *ed = Editor::get_instance();
ed->init();
ed->run();
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2002,2003 Daniel Heck
*
* 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 EDITOR_HH
#define EDITOR_HH
#include <string>
namespace editor
{
void Run();
void DefineFloorGroup (const char *name, const char *descr,
int nentries, char **entries);
void DefineItemGroup (const char *name, const char *descr,
int nentries, char **entries);
void DefineStoneGroup (const char *name, const char *descr,
int nentries, char **entries);
}
#endif

View File

@@ -0,0 +1,335 @@
namespace
{
class EditorState {
public:
EditorState();
~EditorState();
void save (std::ostream &os);
void load (std::istream &is);
private:
};
class DL_Editor
: public display::DisplayLayer
{
public:
DL_Editor()
{
}
~DL_Editor()
{
}
void prepare_draw( const display::WorldArea & )
{
}
void draw( ecl::GC &gc, const display::WorldArea &a, int x, int y )
{
}
void draw_onepass( ecl::GC & gc )
{
//just a test...
//ecl::set_color( gc, 240, 140, 20, 255 );
//ecl::set_flags( gc.flags, GS_ANTIALIAS );
//ecl::line( gc, 0, 0, 320, 240 );
}
void tick( double dtime )
{
}
void new_world( int w, int h )
{
}
};
class EditorDisplay
: public display::CommonDisplay
{
public:
EditorDisplay();
~EditorDisplay() {}
void redraw();
void tick(double /*dtime*/ ) {}
Model *make_model( const std::string &name );
private:
};
class IconBar
: public gui::Container
{
public:
IconBar( const ScreenArea &a, int rows, int cols )
: m_area( a )
, m_rows( rows )
, m_cols( cols )
, m_bgcolor( 150, 150, 150 )
{
}
~IconBar() {}
ScreenArea get_area() const
{
return m_area;
}
// Widget interface.
void draw( ecl::GC &gc, const ecl::Rect &area_ )
{
set_color( gc, m_bgcolor );
box( gc, area_ );
}
private:
// Variables.
ScreenArea m_area;
int m_rows, m_cols;
ecl::RGB m_bgcolor;
};
enum EditMode {
MODE_FLOOR,
MODE_ITEMS,
MODE_STONES,
MODE_ACTORS,
MODE_COUNT
};
struct ObjectGroup {
string name;
string descr;
vector<string> entries;
ObjectGroup( const string &name_,
const string &descr_, const vector<string> entries_
)
: name( name_ )
, descr( descr_ )
, entries( entries_ )
{
}
};
class Tool {
public:
virtual ~Tool() {}
virtual void object_menu() = 0;
virtual void advance_in_group( int offset ) = 0;
virtual void advance_group( int offset ) = 0;
virtual string current_object() = 0;
virtual void on_mousebutton( const SDL_Event &e, V2 worldpos ) = 0;
/// Rounds coordinates for item display. Actor-like tools
/// will want to leave the coordinates intact, grid-like tools
/// could want to round to integers.
virtual void round_coordinates( V2 * worldpos ) = 0;
};
class GridObjectTool
: public Tool
{
public:
// Tool interface
void object_menu();
void advance_in_group( int offset );
void advance_group( int offset );
string current_object()
{
return m_groups[ m_groupidx ].entries[ m_objectidx ];
}
void add_group( const ObjectGroup &g )
{
m_groups.push_back( g );
}
// Constructors
GridObjectTool( GridLayer layer )
{
m_groupidx = m_objectidx = 0;
m_layer = layer;
}
void round_coordinates( V2 * worldpos )
{
(*worldpos)[ 0 ] = round_down<int>( (*worldpos)[ 0 ] );
(*worldpos)[ 1 ] = round_down<int>( (*worldpos)[ 1 ] );
}
void on_mousebutton( const SDL_Event &e, V2 worldpos );
protected:
// Variables
vector<ObjectGroup> m_groups;
size_t m_groupidx;
size_t m_objectidx;
GridLayer m_layer;
};
class FloorTool
: public GridObjectTool
{
public:
FloorTool()
: GridObjectTool(GRID_FLOOR)
{}
};
class ItemTool
: public GridObjectTool
{
public:
ItemTool()
: GridObjectTool( GRID_ITEMS )
{
}
};
class StoneTool
: public GridObjectTool
{
public:
StoneTool ()
: GridObjectTool( GRID_STONES )
{
}
};
class ActorTool
: public Tool
{
public:
// Tool interface.
void object_menu() {}
void advance_in_group( int /*offset*/ ) {}
void advance_group( int /*offset*/ ) {}
string current_object() { return "ac-blackball"; }
void on_mousebutton( const SDL_Event & e, V2 worldpos );
void round_coordinates( V2 * /*worldpos*/ ) {}
private:
};
class Level {
public:
Level( EditorDisplay *display )
: m_display( display )
{
}
private:
// Variables.
EditorDisplay *m_display;
};
class Editor
: private sdl::EventHandler
{
public:
static Editor * get_instance()
{
if( m_instance == NULL ) {
m_instance = new Editor;
}
return m_instance;
}
~Editor();
void init();
void run();
Level *get_level() const
{
return m_level.get();
}
void add_floor_group( const ObjectGroup &g )
{
m_floortool->add_group( g );
}
void add_item_group( const ObjectGroup &g )
{
m_itemtool->add_group( g );
}
void add_stone_group( const ObjectGroup &g )
{
m_stonetool->add_group( g );
}
void set_floor( int x, int y, string const& name );
void set_item( int x, int y, string const& name );
void set_stone( int x, int y, string const& name );
void set_actor( double x, double y, string const& name );
private:
Editor();
/* ---------- Private methods ---------- */
void set_cursor( const string &name );
/// Change cursor face and position based on active tool.
void update_cursor();
void set_mode( EditMode m );
Tool *current_tool();
void new_world( int w, int h );
void scroll( double xoff, double yoff );
void scroll_abs( double x, double y );
/*
** EventHandler interface.
*/
bool on_mousemotion( SDL_Event &e );
bool on_mousebutton( SDL_Event &e );
bool on_keydown( SDL_Event &e );
/*
** Variables.
*/
ScreenArea m_editarea;
ScreenArea m_iconarea;
EditorDisplay m_display;
IconBar m_iconbar;
bool m_quit_editor;
display::SpriteHandle m_cursor;
lua_State *m_lua;
FloorTool *m_floortool;
ItemTool *m_itemtool;
StoneTool *m_stonetool;
ActorTool *m_actortool;
EditMode m_editmode;
vector<Tool *> m_tools;
auto_ptr<Level> m_level;
static Editor *m_instance;
};
}

View File

@@ -0,0 +1,519 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 "errors.hh"
#include "enigma.hh"
#include "ecl.hh"
#include "main.hh"
#include <iostream>
#include <ctime>
#include <set>
using namespace std;
using namespace ecl;
using namespace enigma;
/* -------------------- Game Type -------------------- */
static const char *versionName[GAMET_COUNT+1] = {
"enigma", // same indices as enum GameType
"oxyd1",
"per.oxyd",
"oxyd.extra",
"oxyd.magnum",
0
};
GameType enigma::GetGameType(std::string name) {
GameType type = GAMET_UNKNOWN;
for (int v = 0; v<GAMET_COUNT; ++v) {
if (0 == strcmp(name.c_str(), versionName[v])) {
type = GameType(v);
break;
}
}
return type;
}
std::string enigma::GetGameTypeName(GameType type) {
if (type >= GAMET_FIRST && type <= GAMET_LAST)
return versionName[type];
else
return "unknown";
}
/* -------------------- Direction -------------------- */
Direction enigma::reverse(Direction d) {
static Direction rdir[] = { NODIR, EAST, NORTH, WEST, SOUTH };
return rdir[d+1];
}
Direction enigma::rotate_cw (Direction d)
{
static Direction rdir[] = { NODIR, NORTH, WEST, SOUTH, EAST };
return rdir[d+1];
}
Direction enigma::rotate_ccw (Direction d)
{
static Direction rdir[] = { NODIR, SOUTH, EAST, NORTH, WEST };
return rdir[d+1];
}
Direction
direction_fromto(GridPos source, GridPos target)
{
// source and target have to be adjacent
int dx = target.x-source.x;
int dy = target.y-source.y;
Direction d = NODIR;
if (dx == 0) {
if (dy == -1) d = NORTH;
else if (dy == 1) d = SOUTH;
}
else if (dy == 0) {
if (dx == -1) d = WEST;
else if (dx == 1) d = EAST;
}
ASSERT(d != NODIR, XLevelRuntime,
"direction_fromto: source and target not adjacent");
return d;
}
string enigma::to_suffix(Direction d) {
static const char *sfx[] = { "", "-w", "-s", "-e", "-n" };
return sfx[d+1];
}
/* -------------------- DirectionBits -------------------- */
DirectionBits
enigma::rotate(DirectionBits d, bool clockwise)
{
if (clockwise) {
d = DirectionBits(((d>>1) | (d<<3)) & ALL_DIRECTIONS);
} else {
d = DirectionBits(((d>>3) | (d<<1)) & ALL_DIRECTIONS);
}
return d;
}
/* -------------------- Value implementation -------------------- */
Value::Value(const char* str)
: type(STRING)
{
val.str = new char[strlen(str)+1];
strcpy(val.str, str);
}
Value::~Value()
{
clear();
}
Value::Value(const string& str)
: type(STRING)
{
val.str = new char[str.length()+1];
strcpy(val.str, str.c_str());
}
Value::Value (const Value& other) : type(NIL) {
this->operator=(other);
}
Value& Value::operator= (const Value& other) {
if (this != &other) {
if (other.type == STRING) {
assign(other.val.str);
} else {
clear();
type = other.type;
val = other.val;
}
}
return *this;
}
void Value::assign(const char* s) {
clear();
type = STRING;
val.str = new char[strlen(s)+1];
strcpy(val.str, s);
}
void Value::assign(double d)
{
clear(); type=DOUBLE; val.dval=d;
}
void Value::clear() {
if (type == STRING)
delete[] val.str;
type = NIL;
}
double Value::get_double() const throw()
{
ASSERT(type == DOUBLE, XLevelRuntime, "get_double: type not double");
return val.dval;
}
const char* Value::get_string() const throw()
{
ASSERT(type == STRING, XLevelRuntime, "get_string: type not string");
return val.str;
}
Buffer& enigma::operator<<(Buffer& buf, const Value& val)
{
buf << Uint8(val.get_type());
switch (val.get_type()) {
case Value::NIL:
break;
case Value::DOUBLE:
buf << val.get_double();
break;
case Value::STRING:
{
const char* str = val.get_string();
buf << (Uint16)strlen(str);
buf.write(str, strlen(str));
} break;
}
return buf;
}
// Buffer& enigma::operator>>(Buffer& buf, Value& val)
// {
// Uint8 type = Value::NIL;
// buf >> type;
// switch (type) {
// case Value::NIL:
// // ## fixme
// break;
// case Value::DOUBLE:
// {
// double tmp;
// if (buf >> tmp)
// val = Value(tmp);
// } break;
// case Value::STRING:
// {
// Uint16 len;
// if (buf >> len) {
// char* tmp = new char[len+1];
// tmp[len] = 0;
// if (buf.read(tmp, len))
// val.assign(tmp);
// delete[] tmp;
// }
// } break;
// }
// return buf;
// }
int enigma::to_int(const Value &v) {
switch (v.get_type()) {
case Value::DOUBLE: return round_nearest<int>(v.get_double());
case Value::STRING: return atoi(v.get_string());
default: return 0;
}
}
bool enigma::to_bool(const Value &v) {
return (v.get_type() != Value::NIL);
}
double enigma::to_double(const Value &v) {
switch (v.get_type()) {
case Value::DOUBLE: return v.get_double();
case Value::STRING: return atof(v.get_string());
default: return 0;
}
}
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
const char * enigma::to_string(const Value &v) {
static char buf[30];
switch (v.get_type()) {
case Value::NIL: return "";
case Value::DOUBLE:
snprintf(buf, sizeof(buf), "%f", v.get_double());
return buf;
case Value::STRING: return v.get_string();
default: return 0;
}
}
Direction enigma::to_direction (const Value &v) {
int val = Clamp(to_int(v), 0, 3);
return static_cast<Direction>(val);
}
ostream& enigma::operator<<(ostream& os, const Value& val)
{
switch (val.get_type()) {
case Value::NIL: os << "nil"; break;
case Value::DOUBLE: os << val.get_double(); break;
case Value::STRING: os << val.get_string(); break;
}
return os;
}
/* -------------------- GridPos -------------------- */
GridPos::GridPos(const ecl::V2& p)
: x (round_down<int>(p[0])),
y (round_down<int>(p[1]))
{}
std::ostream& enigma::operator<<(std::ostream& os, const GridPos& val)
{
return os << '(' << val.x << ',' << val.y << ')';
}
/*
516
203
748
*/
GridPos enigma::get_neighbour (GridPos p, int i)
{
ASSERT (i >= 0 && i <= 9, XLevelRuntime, "get_neighbour: index out of bounds");
static int xoff[9] = { 0,0,-1,1,0,-1,1,-1,1 };
static int yoff[9] = { 0,-1,0,0,1,-1,-1,1,1 };
return GridPos(p.x + xoff[i], p.y + yoff[i]);
}
/* -------------------- GridLoc -------------------- */
bool enigma::to_gridloc (const char *str, GridLoc &l) {
GridLoc loc;
const char *numstr = str + 3;
if (strncmp (str, "fl(", 3) == 0)
loc.layer = GRID_FLOOR;
else if (strncmp (str, "it(", 3) == 0)
loc.layer = GRID_ITEMS;
else if (strncmp (str, "st(", 3) == 0)
loc.layer = GRID_STONES;
else
numstr = str;
if (2 != sscanf (numstr, "%d %d", &loc.pos.x, &loc.pos.y))
return false;
l = loc;
return true;
}
/* -------------------- Random numbers -------------------- */
void enigma::Randomize ()
{
srand (time(NULL));
}
void enigma::Randomize (unsigned seed)
{
srand (seed);
}
int enigma::IntegerRand (int min, int max)
{
int r = round_down<int>((max-min+1) * (rand()/(RAND_MAX+1.0)));
return r+min;
}
double enigma::DoubleRand (double min, double max)
{
return min + double(rand())/RAND_MAX * (max-min);
}
/* -------------------- Time & Date -------------------- */
#define MAX_DATE_LENGTH 256
const char *enigma::date(const char *format) { // format see 'man strftime'
static char *result = 0;
char buffer[MAX_DATE_LENGTH];
time_t t;
time(&t);
struct tm *tm = localtime(&t);
strftime(buffer, MAX_DATE_LENGTH, format, tm);
if (result) free(result);
result = strdup(buffer);
return result;
}
/* -------------------- Resource management -------------------- */
namespace
{
struct FontDescr {
// Variables
string name;
string ttf_name;
int ttf_size;
string bitmap_name;
int r, g, b;
// Constructor
FontDescr (const string &name_,
const string &ttf_name_,
int ttf_size_,
const string &bitmap_name_,
int r_, int g_, int b_)
: name (name_),
ttf_name (ttf_name_),
ttf_size (ttf_size_),
bitmap_name (bitmap_name_),
r (r_), g(g_), b(b_)
{}
};
class FontCache : public PtrCache<Font> {
public:
Font *acquire (const std::string &name) {
Font *f = 0;
if (m_fonts.has_key (name)) {
const FontDescr &fd = m_fonts[name];
f = load_ttf (fd.ttf_name, fd.ttf_size, fd.r, fd.g, fd.b);
if (f == 0) {
std::cerr << "Could not load .ttf file " << fd.ttf_name << "\n";
f = load_bmf (fd.bitmap_name);
}
}
else {
f = load_bmf (name);
}
return f;
}
void define_font (const FontDescr &descr) {
remove (descr.name); // remove entry in cache (if any)
if (m_fonts.has_key (descr.name))
m_fonts[descr.name]= descr;
else
m_fonts.insert (descr.name, descr);
}
private:
Font *load_bmf (const string &name) {
string png, bmf;
if (app.resourceFS->findFile(string("fonts/")+name+".png", png) &&
app.resourceFS->findFile(string("fonts/")+name+".bmf", bmf))
{
return ecl::LoadBitmapFont(png.c_str(), bmf.c_str());
}
return 0;
}
Font *load_ttf (const string &name, int ptsize, int r, int g, int b) {
string ttf;
if (app.resourceFS->findFile(string("fonts/") + name, ttf))
return ecl::LoadTTF (ttf.c_str(), ptsize, r, g, b);
return 0;
}
// Variables
ecl::Dict <FontDescr> m_fonts;
};
// ---------- Variables ----------
FontCache font_cache;
ImageCache image_cache;
}
ecl::Surface *ImageCache::acquire (const std::string &name)
{
return ecl::LoadImage(name.c_str());
}
void enigma::DefineFont (const char *name,
const char *ttf_name,
int ttf_size,
const char *bmf_name,
int r, int g, int b)
{
font_cache.define_font (FontDescr (name, ttf_name, ttf_size, bmf_name, r, g, b));
}
ecl::Font *enigma::GetFont (const char *name)
{
return font_cache.get(name);
}
void enigma::ClearFontCache() {
font_cache.clear();
}
ecl::Surface *enigma::LoadImage(const char *name)
{
string filename;
if (app.resourceFS->findImageFile (string(name) + ".png", filename))
return ecl::LoadImage(filename.c_str());
return 0;
}
ecl::Surface *enigma::GetImage(const char *name, const char *ext)
{
string filename;
if (app.resourceFS->findImageFile (string(name) + ext, filename))
return image_cache.get(filename);
return 0;
}
ecl::Surface *enigma::RegisterImage (const char *name, ecl::Surface *s)
{
image_cache.store(name, s);
return s;
}
void enigma::ClearImageCache() {
image_cache.clear();
}

View File

@@ -0,0 +1,946 @@
# Doxyfile 1.2.16
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# General configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME =
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY = doc
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Brazilian, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Korean,
# Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Slovene,
# Spanish, Swedish and Ukrainian.
OUTPUT_LANGUAGE = English
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = YES
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these class will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
# members of a class in the documentation of that class as if those members were
# ordinary class members. Constructors, destructors and assignment operators of
# the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower case letters. If set to YES upper case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are adviced to set this option to NO.
CASE_SENSE_NAMES = YES
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explict @brief command for a brief description.
JAVADOC_AUTOBRIEF = NO
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = NO
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# reimplements.
INHERIT_DOCS = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 8
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consist of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C.
# For instance some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
# only. Doxygen will then generate output that is more tailored for Java.
# For instance namespaces will be presented as packages, qualified scopes
# will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = enigma.cc world.hh objects.cc actors.cc laser.cc items.cc
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl
FILE_PATTERNS =
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
# that are symbolic links (a Unix filesystem feature) are excluded from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH = gfx
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command <filter> <input-file>, where <filter>
# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse.
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the Html help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
# or Internet explorer 4.0+). Note that for large projects the tree generation
# can take a very long time. In such cases it is better to disable this feature.
# Windows users are probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimised for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assigments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_XML = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line and do not end with a semicolon. Such function macros are typically
# used for boiler-plate code, and will confuse the parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tagfiles.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off. Note that this
# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
# recommended to install and use dot, since it yield more powerful graphs.
CLASS_DIAGRAMS = YES
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermedate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
# The CGI_NAME tag should be the name of the CGI script that
# starts the search engine (doxysearch) with the correct parameters.
# A script with this name will be generated by doxygen.
CGI_NAME = search.cgi
# The CGI_URL tag should be the absolute URL to the directory where the
# cgi binaries are located. See the documentation of your http daemon for
# details.
CGI_URL =
# The DOC_URL tag should be the absolute URL to the directory where the
# documentation is located. If left blank the absolute path to the
# documentation, with file:// prepended to it, will be used.
DOC_URL =
# The DOC_ABSPATH tag should be the absolute path to the directory where the
# documentation is located. If left blank the directory on the local machine
# will be used.
DOC_ABSPATH =
# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
# is installed.
BIN_ABSPATH = /usr/local/bin/
# The EXT_DOC_PATHS tag can be used to specify one or more paths to
# documentation generated for other projects. This allows doxysearch to search
# the documentation for these projects as well.
EXT_DOC_PATHS =

View File

@@ -0,0 +1,323 @@
/*
* Copyright (C) 2002,2004 Daniel Heck
*
* 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 ENIGMA_HH
#define ENIGMA_HH
/*
* This file contains declarations for facilities used by several
* different parts of the program, like common types and constants,
* and routines for resource management.
*/
#include "fwd.hh"
#include "file.hh"
#include "ecl_fwd.hh"
#include "ecl_math.hh"
#include "ecl_util.hh"
#include "ecl_cache.hh"
#define NUMENTRIES(array) (sizeof(array)/sizeof(*array))
namespace enigma
{
/* -------------------- Various types -------------------- */
enum Difficulty {
DIFFICULTY_EASY = 1,
DIFFICULTY_HARD = 2,
DIFFICULTY_ANY = 3
};
/*! Enigma can run its own levels but also emulate various
versions of Oxyd. All these games behave similarly, but there
are a few differences in object behaviour, visual appearance,
etc. */
enum GameType {
GAMET_FIRST,
GAMET_ENIGMA = GAMET_FIRST,
GAMET_OXYD1,
GAMET_PEROXYD,
GAMET_OXYDEXTRA,
GAMET_OXYDMAGNUM,
GAMET_LAST = GAMET_OXYDMAGNUM,
GAMET_COUNT,
GAMET_UNKNOWN
};
GameType GetGameType(std::string name);
std::string GetGameTypeName(GameType type);
/* -------------------- Resource Management -------------------- */
class ImageCache : public ecl::PtrCache<ecl::Surface> {
public:
using ecl::PtrCache<ecl::Surface>::store;
ecl::Surface *acquire(const std::string &name);
};
/* The `Get...' functions return a pointer to a cached copy of the
font or image. The `Load...' functions load a new copy which
you must deallocate yourself. */
void DefineFont (const char *name,
const char *ttf_name,
int ttf_size,
const char *bmf_name,
int r, int g, int b);
ecl::Font *GetFont (const char *name);
void ClearFontCache();
ecl::Surface *LoadImage (const char *name);
ecl::Surface *GetImage (const char *name, const char *ext = ".png");
ecl::Surface *RegisterImage (const char *name, ecl::Surface *s);
void ClearImageCache();
/* -------------------- Direction, DirectionBits -------------------- */
enum Direction {
NODIR = -1,
WEST = 0,
SOUTH = 1,
EAST = 2,
NORTH = 3,
};
Direction reverse (Direction d);
Direction rotate_cw (Direction d);
Direction rotate_ccw (Direction d);
std::string to_suffix(Direction d);
enum DirectionBits {
NODIRBIT = 0,
WESTBIT = 1 << WEST,
SOUTHBIT = 1 << SOUTH,
EASTBIT = 1 << EAST,
NORTHBIT = 1 << NORTH,
ALL_DIRECTIONS = 15
};
DirectionBits rotate (DirectionBits d, bool clockwise);
DirectionBits to_bits (Direction d);
bool has_dir (DirectionBits db, Direction dir);
/* -------------------- Value -------------------- */
class Value {
public:
enum Type { NIL, DOUBLE, STRING };
Value() : type(NIL) {}
Value(double d) : type(DOUBLE) { val.dval = d; }
Value(const char* str);
Value(const std::string& str);
~Value();
Value(const Value& v);
Value& operator=(const Value& v);
void assign(double d);
void assign(const char* s);
Type get_type() const { return type; }
double get_double() const throw();
const char* get_string() const throw();
private:
void clear();
// Variables
Type type;
union {
double dval;
char* str;
} val;
};
ecl::Buffer& operator<<(ecl::Buffer& buf, const Value& val);
// ecl::Buffer& operator>>(ecl::Buffer& buf, Value& val);
std::ostream& operator<<(std::ostream& os, const Value& val);
bool to_bool(const Value &v);
int to_int(const Value &v);
double to_double(const Value &v);
const char *to_string(const Value &v);
Direction to_direction (const Value &v);
/* -------------------- Timers -------------------- */
/* Interface for time event handlers. */
class TimeHandler {
public:
virtual ~TimeHandler() {}
virtual void tick (double /*dtime*/) {}
virtual void alarm() {}
};
/* -------------------- GridPos -------------------- */
struct GridPos {
// Variables
int x, y;
// Methods
explicit GridPos(int xx=0, int yy=0);
explicit GridPos(const ecl::V2& p);
void move(Direction dir);
ecl::V2 center() const;
};
GridPos move(GridPos p, Direction dir);
GridPos move(GridPos p, Direction dir, Direction dir2);
bool operator== (GridPos a, GridPos b);
bool operator != (GridPos a, GridPos b);
bool operator< (GridPos a, GridPos b);
/* 516
203
748 */
GridPos get_neighbour (GridPos p, int i);
// source and target have to be adjacent
Direction direction_fromto(GridPos source, GridPos target);
std::ostream& operator<<(std::ostream& os, const GridPos& val);
// ---------- GridLayer ----------
enum GridLayer {
GRID_FLOOR,
GRID_ITEMS,
GRID_STONES,
GRID_COUNT
};
enum GridLayerBits {
GRID_FLOOR_BIT = 1,
GRID_ITEMS_BIT = 2,
GRID_STONES_BIT = 4
};
// ---------- GridLoc ----------
struct GridLoc {
// Variables
GridPos pos;
GridLayer layer;
// Constructor
GridLoc(GridLayer l = GRID_FLOOR, GridPos p = GridPos());
};
/*! Converts strings like "it(10 10)", "st(5 2)" to GridLoc
structures. */
bool to_gridloc (const char *str, GridLoc &loc);
/* -------------------- Random Numbers -------------------- */
void Randomize ();
void Randomize (unsigned seed);
int IntegerRand (int min, int max);
double DoubleRand (double min, double max);
/* -------------------- Time & Date -------------------- */
const char *date(const char *format); // format see 'man strftime'
/* ==================== Inline definitions ==================== */
inline DirectionBits to_bits(Direction d) {
if (d==NODIR)
return NODIRBIT;
return DirectionBits(1 << d);
}
inline bool has_dir(DirectionBits db, Direction dir) {
return (db & to_bits(dir)) != 0;
}
// ---------- GridPos ----------
inline GridPos::GridPos(int xx, int yy)
: x(xx), y(yy)
{}
inline void GridPos::move(Direction dir) {
switch(dir) {
case NORTH: y--; break;
case SOUTH: y++; break;
case EAST: x++; break;
case WEST: x--; break;
case NODIR: break;
}
}
inline ecl::V2 GridPos::center() const {
return ecl::V2(x+.5, y+.5);
}
inline GridPos move(GridPos p, Direction dir) {
GridPos tmp = p;
tmp.move(dir);
return tmp;
}
inline GridPos move(GridPos p, Direction dir, Direction dir2) {
GridPos tmp = p;
tmp.move(dir);
tmp.move(dir2);
return tmp;
}
inline bool operator == (GridPos a, GridPos b) {
return (a.x==b.x && a.y==b.y);
}
inline bool operator != (GridPos a, GridPos b) {
return (a.x!=b.x || a.y!=b.y);
}
inline bool operator< (GridPos a, GridPos b) {
return ((a.y<<16) + a.x) < ((b.y<<16) + b.x);
}
// ---------- GridLoc ----------
inline GridLoc::GridLoc(GridLayer l, GridPos p)
: pos(p), layer(l)
{}
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -0,0 +1 @@
enigma ICON "enigma.ico"

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2002,2003,2004,2005 Daniel Heck
*
* 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 ERRORS_HH
#define ERRORS_HH
#include "ecl_error.hh"
#include <string>
namespace enigma {
/* -------------------- Exceptions -------------------- */
class XLevelLoading : public ecl::XGeneric {
public:
XLevelLoading (const std::string &msg)
: XGeneric (msg)
{}
};
class XLevelPackInit : public ecl::XGeneric {
public:
XLevelPackInit (const std::string &msg)
: XGeneric (msg)
{}
};
class XLevelRuntime : public ecl::XGeneric {
public:
XLevelRuntime (const std::string &msg)
: XGeneric (msg)
{}
};
class XFrontend : public ecl::XGeneric {
public:
XFrontend (const std::string &msg)
: XGeneric (msg)
{}
};
} // namespace enigma
#endif

View File

@@ -0,0 +1,372 @@
/*
* Copyright (C) 2002,2003,2004,2005 Daniel Heck
*
* 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 "file.hh"
#include "enigma.hh"
#include "video.hh"
#include "main.hh"
#include "ecl_system.hh"
#include "config.h"
#include <sys/types.h>
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#include <algorithm>
#include <ios>
#include <iostream>
#include <fstream>
using namespace enigma;
using namespace ecl;
using namespace std;
DirIter::DirIter() {}
DirIter::~DirIter() {}
namespace
{
/* -------------------- DirIter (POSIX) -------------------- */
#ifdef HAVE_DIRENT_H
class DirIterOS : DirIter {
public:
DirIterOS (const std::string &path) : m_dir (NULL), m_entry (NULL) {
open (path);
dir_path = path;
}
virtual ~DirIterOS () {
if (m_dir != NULL)
closedir (m_dir);
}
virtual bool open (const std::string &path) {
m_dir = opendir (path.c_str());
return m_dir != 0;
}
virtual bool get_next (DirEntry &entry) {
if (m_dir == 0) return false;
m_entry = readdir(m_dir);
if (m_entry != NULL) {
entry.name = m_entry->d_name;
// entry.is_dir = false;
entry.is_dir = ecl::FolderExists(dir_path + "/" + entry.name);
return true;
}
return false;
}
private:
std::string dir_path;
DIR *m_dir;
struct dirent *m_entry;
};
/* -------------------- DirIter (Win32) -------------------- */
#elif defined (_MSC_VER)
#include <windows.h>
class DirIterOS : DirIter {
public:
DirIterOS (const std::string &path)
: m_handle (INVALID_HANDLE_VALUE)
{
open (path);
}
~DirIterOS () {
close();
}
bool open (const std::string &path) {
std::string glob (path);
glob += "\\*.*";
m_handle = FindFirstFile (glob.c_str(), &m_dir);
return m_handle != INVALID_HANDLE_VALUE;
}
bool get_next (DirEntry &entry) {
if (m_handle != INVALID_HANDLE_VALUE) {
entry.name = m_dir.cFileName;
// entry.is_dir = false;
entry.is_dir = m_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
if (!FindNextFile (m_handle, &m_dir))
close();
return true;
}
return false;
}
private:
void close () {
if (m_handle != INVALID_HANDLE_VALUE) {
FindClose (m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
}
// Variables.
WIN32_FIND_DATA m_dir;
HANDLE m_handle;
};
#endif
}
DirIter * DirIter::instance(const std::string &path) {
return reinterpret_cast<DirIter *>(new DirIterOS(path));
}
/* -------------------- FileHandle_Dir -------------------- */
//
// FileHandle_Dir::FileHandle_Dir (const std::string &name)
// : m_name (name)
// {
// }
//
// bool FileHandle_Dir::exists() const
// {
// return true;
// }
//
// void FileHandle_Dir::read (ByteVec &buffer)
// {
// std::ifstream ifs(m_name.c_str());
// enigma::readfile (ifs, buffer);
// }
//
/* -------------------- GameFS implementation -------------------- */
GameFS::GameFS()
: entries ()
{
}
void GameFS::append_dir (const string &path)
{
std::string full_path = ecl::ExpandPath (path);
entries.push_back (FSEntry (FS_DIRECTORY, full_path));
}
void GameFS::prepend_dir (const string &path)
{
std::string full_path = ecl::ExpandPath (path);
entries.insert (entries.begin(), FSEntry (FS_DIRECTORY, full_path));
}
void GameFS::prepend_zip (const std::string &filename)
{
std::string path = ecl::ExpandPath (filename);
entries.insert (entries.begin(), FSEntry (FS_ZIPFILE, path));
}
void GameFS::setDataPath (const string &p)
{
clear();
std::vector<std::string> datapaths;
split_copy (p, *ecl::PathsSeparator, back_inserter(datapaths));
for (unsigned i=0; i<datapaths.size(); ++i)
append_dir (datapaths[i]);
}
std::string GameFS::getDataPath() {
std::string path;
for (unsigned i=0, size=entries.size(); i < size; ++i) {
const FSEntry &e = entries[i];
if (i>0)
path += ecl::PathsSeparator;
path += e.location;
}
return path;
}
std::vector<std::string> GameFS::getPaths() {
std::vector<std::string> paths;
for (unsigned i=0, size=entries.size(); i < size; ++i) {
const FSEntry &e = entries[i];
paths.push_back(e.location);
}
return paths;
}
bool GameFS::findFile (const string &filename, string &dest) const
{
for (unsigned i=0; i<entries.size(); ++i) {
const FSEntry &e = entries[i];
switch (e.type) {
case FS_DIRECTORY:
{
string complete_name = e.location + ecl::PathSeparator + filename;
if (ecl::FileExists(complete_name))
{
dest = complete_name;
return true;
}
} break;
case FS_ZIPFILE:
{
} break;
}
}
return false;
}
bool GameFS::findFile (const string &filename, string &dest,
std::auto_ptr<std::istream> &isresult) const {
std::string::size_type slpos = filename.rfind('/');
std::string zipName;
std::string zippedFilename1, zippedFilename2;
bool searchZip = false;
std::string complete_name;
if (slpos != std::string::npos) {
// file may be zipped - for "levels/Sokoban/mic_60.xml" we will look for
// "mic_60.xml" and "Sokoban/mic_60.xml" at "levels/Sokoban.zip"
searchZip = true;
zipName = filename.substr(0, slpos) + ".zip";
zippedFilename1 = filename.substr(slpos + 1);
std::string::size_type slpos2 = filename.rfind('/', slpos-1);
zippedFilename2 = filename.substr(slpos2 + 1);
}
for (unsigned i=0; i<entries.size(); ++i) {
const FSEntry &e = entries[i];
switch (e.type) {
case FS_DIRECTORY: {
complete_name = e.location + ecl::PathSeparator + filename;
if (ecl::FileExists(complete_name)) {
dest = complete_name;
return true;
} else if (searchZip){
complete_name = e.location + ecl::PathSeparator + zipName;
if (ecl::FileExists(complete_name) &&
findInZip(complete_name, zippedFilename1,
zippedFilename2, dest, isresult)) {
return true;
}
}
} break;
case FS_ZIPFILE: {
} break;
}
}
return false;
}
// enigma::FileHandle *GameFS::findFile (const FileName &n)
// {
// string fname;
// if (findFile (n, fname)) {
// return new FileHandle_Dir (fname);
// }
// return 0;
// }
std::string GameFS::findFile(const string &filename)
{
string found_file;
if (!findFile(filename, found_file)) {
enigma::Log << "File not found: " << filename << endl;
return filename;
}
return found_file;
}
std::list <string>
GameFS::findSubfolderFiles(const string &folder, const string &filename) const
{
std::list <string> matches;
for (unsigned i=0; i<entries.size(); ++i) {
const FSEntry &e = entries[i];
switch (e.type) {
case FS_DIRECTORY: {
string complete_name = e.location + ecl::PathSeparator + folder;
if (ecl::FolderExists(complete_name)) {
DirIterOS iter (complete_name);
DirEntry entry;
while (iter.get_next (entry)) {
if (entry.name != "." && entry.name != "..") {
string tmp_name = complete_name + ecl::PathSeparator
+ entry.name + ecl::PathSeparator + filename;
if (ecl::FileExists (tmp_name))
matches.push_back (tmp_name);
}
}
}
break;
}
case FS_ZIPFILE:
break;
}
}
return matches;
}
/* First search in video mode specific directory, then in "gfx/". */
bool GameFS::findImageFile (const string &basename, string &filename)
{
const video::VMInfo *vminfo = video::GetInfo();
string fname = string(vminfo->gfxdir) + basename;
if (!findFile(fname, filename)) {
fname = string ("gfx/") + basename;
return findFile(fname, filename);
}
return true;
}
/* -------------------- Helper functions -------------------- */
std::istream &
enigma::Readfile (std::istream &is, ByteVec &dest, int blocksize)
{
size_t len = dest.size();
int nread=0;
do {
dest.resize(dest.size() + blocksize);
is.read (&dest[len], blocksize);
nread = is.gcount();
len += nread;
} while (nread == blocksize);
dest.resize(len);
return is;
}
bool enigma::Copyfile(std::string fromPath, std::string toPath) {
ByteVec content;
ifstream ifs(fromPath.c_str(), ios::binary | ios::in);
Readfile (ifs, content);
ofstream ofs(toPath.c_str(), ios::binary | ios::out);
ofs.write(&content[0], content.size());
ofs.close();
return !ofs.fail();
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 FILE_HH_INCLUDED
#define FILE_HH_INCLUDED
#include <iosfwd>
#include <vector>
#include <list>
#include <memory>
#include "ecl_error.hh"
namespace enigma
{
typedef std::vector<char> ByteVec;
typedef std::string FileName;
using std::string;
enum FSType {
FS_DIRECTORY,
FS_ZIPFILE,
};
struct FSEntry {
FSType type;
std::string location;
FSEntry (FSType type_, const std::string &location_)
: type (type_), location (location_)
{}
};
struct DirEntry {
std::string name;
bool is_dir;
};
class DirIter {
public:
static DirIter * instance(const std::string &path);
virtual ~DirIter ();
virtual bool open(const std::string &path) = 0;
virtual bool get_next (DirEntry &entry) = 0;
protected:
DirIter();
};
/* -------------------- FileHandles --------------------*/
//
// class FileHandle {
// public:
// virtual ~FileHandle() {}
//
// // FileHandle interface.
// virtual bool exists() const = 0;
// virtual void read (ByteVec &buffer) = 0;
// };
//
// class FileHandle_Dir : public FileHandle {
// std::string m_name;
// std::string m_path;
// bool m_exists; // File exists
//
// public:
// FileHandle_Dir (const std::string &name);
//
// // FileHandle interface.
// bool exists() const;
// void read (ByteVec &buffer);
// };
//
// class FileHandle_Zip : public FileHandle {
// public:
// FileHandle_Zip();
//
// // FileHandle interface.
// bool exists() const;
// void read (ByteVec &buffer);
// };
/**
* A GameFS is a list of directories that are searched when
* Enigma tries to find a data file (for example a png image). The
* data path is usually specified as a single string like
* ".:~/.enigma:/usr/local/share/enigma" list with entries separated by
* the OS dependent separating ":" or ";" (ecl::PathsSeparator).
*
* <p> When searching for files, the file name may of course include
* subdirectory names. For example:</p>
* <p><code>
* findFile("graphics/bomb.png")
* -> "/usr/local/share/enigma/graphics/bomb.png"
* or "/home/user/.enigma/graphics/bomb.png"
* </code></p>
* <p> Preconfigured GameFS instances are accessable via the Application
* instance, f.e. <code>app.resourceFS</code></p>
*/
class GameFS {
public:
GameFS();
void clear() { entries.clear(); }
void append_dir (const string &path);
void prepend_dir (const string &path);
void prepend_zip (const string &filename);
void setDataPath (const string &p);
std::string getDataPath();
std::vector<std::string> getPaths();
/**
* Search first occurence of a file on the GameFS. The file can be
* a path component like "levels/index.lua".
* @param filename the searched filename
* @param dest the expanded full path of the first occurence.
* @return has a file been found.
*/
bool findFile(const string &filename, string &dest) const;
/**
* Search first occurence of a file on the GameFS. The file can be
* a path component like "levels/e1/hello.lua". Zip archives are searched,
* too. The path above would be looked up in "levels/e1.zip" as "hello.lua"
* and as "e1/hello.lua". Plain files preceed zipped onces on every
* search directory.
* @param filename the searched filename
* @param dest the expanded full path of the first occurence.
* @param isptr an opened istream of the first occurence in case
* the file is zipped
* @return has a file been found.
*/
bool findFile(const string &filename, string &dest,
std::auto_ptr<std::istream> &isptr) const;
// FileHandle *findFile (const FileName &);
/**
* Search first occurence of a file on the GameFS. The file can be
* a path component like "levels/index.lua". If the file can not be
* located a message is logged - use this method if you have good
* reasons to expect the file being found.
* @param filename the searched filename
* @return the expanded full path of the first occurence or ""
*/
std::string findFile(const string &filename);
/**
* Lists the paths of all files with a given name that reside in
* a subfolder of the given basepath.
* The basepath itself is not searched for files. Only direct one
* level deep subfolders are searched. Of course all components of
* the GameFS are searched for! Used for searching index like files.
* @param folder the basefolder path component, f.e. "levels"
* @param filename the searched filename, f.e. "index.lua"
* @return a list of fully expanded paths to matching files
*/
std::list <string> findSubfolderFiles (const string &folder,
const string &filename) const;
/** Find an image file named `f' in the resolution-dependent
* graphics directories "gfx??" or in "gfx" and store the
* path in `dst_path'. Returns true if successful.
*/
bool findImageFile (const FileName &f, std::string &dst_path);
private:
// Variables
std::vector<FSEntry> entries;
};
/* -------------------- Helper functions -------------------- */
/*! Load a complete file/input stream `is' into `dst'. */
std::istream &Readfile (std::istream &is, ByteVec &dst, int blocksize=512);
bool Copyfile(std::string fromPath, std::string toPath);
// banned code to file_zip.cc due to macro clashes
bool findInZip(std::string zipPath, std::string zippedFilename1,
std::string zippedFilename2, string &dest,
std::auto_ptr<std::istream> &isresult);
bool writeToZip(std::ostream &zipStream, std::string filename, unsigned size, std::istream &contents);
bool readFromZipStream(std::istream &zipFile, std::ostream &contents);
} // namespace enigma
#endif

View File

@@ -0,0 +1,81 @@
/*
* 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.
*/
// Whoever knows how to integrate this code into file.cc should do it!
// The problem to solve is the clashing macro definitions in "zipios-config.h"
// and "config.h"
// The static var "zip" should be a private ivar of GameFS
#include "file.hh"
#include "zipios++/zipfile.h"
#include "zipios++/zipoutputstream.h"
#include "zipios++/zipoutputstreambuf.h"
#include "zipios++/zipinputstreambuf.h"
#include <istream>
#include <ostream>
#include <ctime>
using namespace enigma;
using namespace std;
using namespace zipios;
static std::auto_ptr<zipios::ZipFile> zip;
static std::string lastZipPath;
bool enigma::findInZip(std::string zipPath, std::string zippedFilename1,
std::string zippedFilename2, string &dest,
std::auto_ptr<std::istream> &isresult) {
// reuse last opened zip if possible
if (lastZipPath != zipPath) {
zip.reset (new zipios::ZipFile (zipPath));
lastZipPath = zipPath;
}
std::auto_ptr<istream> isptr (zip->getInputStream (zippedFilename2));
if(isptr.get() != 0) {
isresult = isptr;
dest = zippedFilename2;
return true;
}
isptr.reset(zip->getInputStream (zippedFilename1));
if(isptr.get() != 0) {
isresult = isptr;
dest = zippedFilename1;
return true;
}
return false;
}
bool enigma::writeToZip(std::ostream &zipStream, std::string filename, unsigned size, std::istream &contents) {
ZipOutputStreambuf zos(zipStream.rdbuf());
ZipCDirEntry ze(filename);
ze.setSize(size);
ze.setTime(time(NULL)); // seems not to be implemented in zipios !
zos.putNextEntry(ze);
std::ostream ozs( &zos );
ozs << contents.rdbuf();
return true;
}
bool enigma::readFromZipStream(std::istream &zipFile, std::ostream &contents) {
ZipInputStreambuf zis(zipFile.rdbuf());
std::istream is( &zis );
contents << is.rdbuf();
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 FLOORS_HH_INCLUDED
#define FLOORS_HH_INCLUDED
namespace world
{
enum FloorFlags {
flf_default = 0,
flf_indestructible = 0x01,
flf_no_items = 0x02,
flf_norespawn = 0x04,
};
enum FloorID {
fl_abyss,
fl_water,
fl_swamp,
fl_space,
fl_thief
};
enum FloorFireType {
flft_default = 0,
flft_burnable = 0x01, // Floor behaves as if it-burnable lies on it.
flft_ignitable = 0x02, // Ignite on bomb explosions. Not used.
flft_noash = 0x04, // Don't leave ash behind; floor might burn again.
flft_burnagain = 0x08, // Not used yet, nor implemented; see flft_noash.
flft_eternal = 0x10, // Fire doesn't stop burning by itself.
flft_secure = 0x20, // Secures fire and heat-effects when neighbors burn.
flft_fastfire = 0x40, // Suppress use of fire_countdown, resulting in faster fire.
flft_initfire = 0x80 // Start burning (forcefire on init-message).
// Note that only flft_burnable and flft_noash are really used as traits.
// The others are 0 by default for all floors, but used as selectors
// for has_firetype. (Future use for special floors not excluded.)
};
struct FloorTraits {
// Variables
string name;
double friction;
double mousefactor;
FloorFlags flags;
FloorFireType firetype;
string firetransform; // fire on the same tile
string heattransform; // fire on neighboring tile
// Constructor
FloorTraits (const char *n, double f, double m,
FloorFlags flags_, FloorFireType flft = flft_default,
const char *ft = "", const char *ht = "")
: name(n), friction(f), mousefactor(m), flags(flags_),
firetype(flft), firetransform(ft), heattransform(ht)
{}
};
enum FloorHeatFlags {
// These are used as second argument to try_heating and try_ignite,
// they contain the context of a call.
flhf_message = 0, // Source is a user-message.
flhf_fire = 0x01, // Source is fire.
flhf_first = 0x02, // First heat message from a burning site.
flhf_last = 0x04 // Last heat message from a burning site.
};
class Floor : public GridObject {
public:
Floor (const FloorTraits &tr);
Floor (const char *kind, double friction_, double mfactor,
FloorFlags flags=flf_default, FloorFireType flft = flft_default,
const char *firetransform_ = "", const char *heattransform_ = "");
// Object interface
Floor *clone();
void dispose();
virtual Value message(const string& msg, const Value &val);
// Floor interface
virtual ecl::V2 process_mouseforce (Actor *a, ecl::V2 force);
virtual void add_force(Actor *, ecl::V2 &); // Note: actor = 0 must be allowed!
virtual void on_drop (Item *) {}
virtual void on_pickup (Item *) {}
virtual void stone_change(Stone *) {}
virtual void actor_contact (Actor *) {}
virtual double friction() const;
virtual double mousefactor() const;
virtual void get_sink_speed (double &sinkspeed, double &raisespeed) const;
virtual bool is_destructible() const;
virtual void animcb();
void on_burnable_animcb(bool justIgnited); // Called by burnable-items on it.
protected:
// GridObject interface
void set_model (const std::string &mname);
display::Model *get_model ();
void kill_model (GridPos p);
// Fire interface
virtual bool has_firetype(FloorFireType selector);
virtual string get_firetransform();
virtual string get_heattransform(bool override_mode);
void heat_neighbor(Direction dir, FloorHeatFlags flhf);
int get_fire_countdown();
virtual bool on_heattransform(Direction sourcedir, FloorHeatFlags flhf);
private:
virtual void on_actorhit(Actor * /*a*/) {}
// Fire logic
Value try_heating(Direction sourcedir, FloorHeatFlags flhf);
Value try_ignite(Direction sourcedir, FloorHeatFlags flhf);
Value force_fire();
Value stop_fire(bool is_message);
FloorTraits traits;
bool heating_animation;
int fire_countdown; // used to delay ignition, default is 1.
};
void InitFloors();
}
#endif

View File

@@ -0,0 +1,55 @@
//======================================================================
// Copyright (C) 2002 Daniel Heck
//
// 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 FWD_HH
#define FWD_HH
namespace enigma_player
{
class Inventory;
}
namespace enigma_levels
{
class Level;
class LevelPack;
}
namespace tools
{
class Value;
}
namespace display
{
class Model;
}
namespace world
{
class Object;
class Stone;
class Floor;
class Item;
class Actor;
struct ActorInfo;
struct StoneContact;
struct Impulse;
struct Message;
}
#endif

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 "game.hh"
#include "errors.hh"
#include "main.hh"
#include "options.hh"
#include "video.hh"
#include "client.hh"
#include "server.hh"
#include "world.hh"
#include "sound.hh"
#include "lev/PersistentIndex.hh"
#include "ecl_sdl.hh"
#include <cassert>
using namespace enigma;
using namespace std;
/* -------------------- Global variables -------------------- */
namespace
{
Uint32 last_tick_time;
}
/* -------------------- Level previews -------------------- */
bool game::DrawLevelPreview (ecl::GC &gc, lev::Proxy *levelProxy)
{
bool success = false;
sound::TempDisableSound();
try {
server::Msg_LoadLevel (levelProxy, true);
display::DrawAll(gc);
success = true;
}
catch (XLevelLoading &err) {
// log the message as we cannot display it on the screen
Log << "DrawLevelPreview load error:\n" << err.what();
success = false;
}
sound::TempReEnableSound();
return success;
}
/* -------------------- Functions -------------------- */
void game::StartGame ()
{
lev::Index *ind = lev::Index::getCurrentIndex();
server::InitNewGame();
video::HideMouse();
sdl::TempInputGrab grab(enigma::Nograb ? SDL_GRAB_OFF : SDL_GRAB_ON);
server::Msg_LoadLevel(ind->getCurrent(), false);
double dtime = 0;
last_tick_time=SDL_GetTicks();
while (!client::AbortGameP()) {
try {
client::Tick (dtime);
server::Tick (dtime);
}
catch (XLevelRuntime& err) {
client::Msg_Error (string("Server Error: level runtime error:\n")
+ err.what());
server::Msg_Panic(true);
}
int sleeptime = 10 - (SDL_GetTicks()-last_tick_time);
if (sleeptime >= 3) // only sleep if relatively idle
SDL_Delay(sleeptime);
Uint32 current_tick_time = SDL_GetTicks();
dtime = (current_tick_time - last_tick_time)/1000.0;
if (abs(1-dtime/0.01) < 0.2) {
// less than 20% deviation from desired frame time?
dtime = 0.01;
last_tick_time += 10;
} else {
last_tick_time = current_tick_time;
}
if (dtime > 500.0) /* Time has done something strange, perhaps
run backwards */
dtime = 0.0;
// else if (dtime > 0.5)
// dtime = 0.5;
}
// add last played level
lev::PersistentIndex::addCurrentToHistory();
video::ShowMouse();
}
void game::ResetGameTimer()
{
last_tick_time=SDL_GetTicks();
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 ENIGMA_GAME_HH
#define ENIGMA_GAME_HH
#include "fwd.hh"
#include "ecl_fwd.hh"
#include "lev/Proxy.hh"
namespace enigma_game
{
using enigma_levels::LevelPack;
using enigma_levels::Level;
void ResetGameTimer();
void StartGame();
/*! Draw a level preview into video::BackBuffer(). Return true if
successful. */
bool DrawLevelPreview (ecl::GC &, enigma::lev::Proxy *levelProxy);
}
namespace enigma
{
namespace game = enigma_game;
}
#endif

View File

@@ -0,0 +1,114 @@
/*
* 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 "gui/ErrorMenu.hh"
#include "enigma.hh"
#include "video.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- HelpMenu -------------------- */
ErrorMenu::ErrorMenu(std::string message, std::string quitTitle) :
text (message), rejectQuit (false), laterQuit (false),
quit (new gui::StaticTextButton(quitTitle, this)) {
const video::VMInfo *vminfo = video::GetInfo();
add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40));
}
ErrorMenu::ErrorMenu(std::string message, std::string quitTitle, std::string rejectTitle) :
text (message), rejectQuit (false), laterQuit (false),
quit (new gui::StaticTextButton(quitTitle, this)),
reject (new gui::StaticTextButton(rejectTitle, this)) {
const video::VMInfo *vminfo = video::GetInfo();
add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40));
add(reject, Rect(vminfo->width-340, vminfo->height-60, 150, 40));
}
ErrorMenu::ErrorMenu(std::string message, std::string quitTitle, std::string rejectTitle,
std::string laterTitle) :
text (message), rejectQuit (false), laterQuit (false),
quit (new gui::StaticTextButton(quitTitle, this)),
reject (new gui::StaticTextButton(rejectTitle, this)),
later (new gui::StaticTextButton(laterTitle, this)) {
const video::VMInfo *vminfo = video::GetInfo();
add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40));
add(later, Rect(vminfo->width-340, vminfo->height-60, 150, 40));
add(reject, Rect(vminfo->width-510, vminfo->height-60, 150, 40));
}
bool ErrorMenu::isRejectQuit() {
return rejectQuit;
}
bool ErrorMenu::isLaterQuit() {
return laterQuit;
}
bool ErrorMenu::on_event (const SDL_Event &e)
{
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) {
Menu::quit();
return true;
}
return false;
}
void ErrorMenu::on_action (gui::Widget *w) {
if (w == quit) {
Menu::quit();
} else if (w == reject) {
rejectQuit = true;
Menu::quit();
} else if (w == later) {
laterQuit = true;
Menu::quit();
}
}
void ErrorMenu::draw_background (ecl::GC &gc) {
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
vector<string> lines;
ecl::split_copy (text, '\n', back_inserter(lines));
int x = 60;
int y = 60;
int yskip = 25;
const video::VMInfo *vminfo = video::GetInfo();
int width = vminfo->width - 120;
for (unsigned i=0; i<lines.size(); ) {
std::string::size_type breakPos = breakString(f, lines[i],
" ", width);
f->render(gc, x, y, lines[i].substr(0,breakPos).c_str());
y += yskip;
if (breakPos != lines[i].size()) {
// process rest of line
lines[i] = lines[i].substr(breakPos);
} else {
// process next line
i++;
}
}
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,50 @@
/*
* 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 GUI_ERRORMENU_HH_INCLUDED
#define GUI_ERRORMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "ecl.hh"
namespace enigma { namespace gui {
class ErrorMenu : public Menu {
public:
ErrorMenu (std::string message, std::string quitTitle);
ErrorMenu (std::string message, std::string quitTitle, std::string rejectTitle);
ErrorMenu (std::string message, std::string quitTitle, std::string rejectTitle,
std::string laterTitle);
bool isRejectQuit();
bool isLaterQuit();
private:
bool on_event (const SDL_Event &e);
void on_action (gui::Widget *w);
void draw_background (ecl::GC &gc);
std::string text;
gui::Widget *quit;
gui::Widget *reject;
gui::Widget *later;
bool rejectQuit;
bool laterQuit;
};
}} // namespace enigma::gui
#endif // GUI_ERRORMENU_HH_INCLUDED

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 "gui/GameMenu.hh"
#include "gui/OptionsMenu.hh"
#include "gui/LevelInspector.hh"
#include "client.hh"
#include "display.hh"
#include "ecl.hh"
#include "main.hh"
#include "nls.hh"
#include "server.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "lev/Proxy.hh"
#include <cassert>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- GameMenu -------------------- */
GameMenu::GameMenu (int zoomxpos_, int zoomypos_)
: zoomed(0),
zoomxpos(zoomxpos_),
zoomypos(zoomypos_)
{
resume = new gui::StaticTextButton(N_("Resume Level"), this);
restart = new gui::StaticTextButton(N_("Restart Level"), this);
options = new gui::StaticTextButton(N_("Options"), this);
info = new gui::StaticTextButton(N_("Level Info"), this);
abort = new gui::StaticTextButton(N_("Abort Level"), this);
add(resume, Rect(0,0,180,40));
add(restart, Rect(0,45,180,40));
add(options, Rect(0,90,180,40));
add(info, Rect(0,135,180,40));
add(abort, Rect(0,180,180,40));
center();
}
GameMenu::~GameMenu() {
delete(zoomed);
}
void GameMenu::draw_background(ecl::GC &gc)
{
const video::VMInfo *vminfo = video::GetInfo();
if (!zoomed) {
Rect game_area = display::GetGameArea();
int part_width = game_area.w/3;
int part_height = (part_width*vminfo->height)/vminfo->width;
if (part_height > game_area.h) {
part_height = game_area.h/3;
part_width = (part_height*vminfo->width)/vminfo->height;
assert(part_width <= game_area.w);
}
// randomly choose ball offset
int x, y;
for (int trials = 5; trials; --trials) {
x = IntegerRand(0, 5);
y = IntegerRand(0, 3);
// try to avoid menu-ball overlap:
if (x<2 || x>3 || y<1 || y>2 || (trials == 1)) {
int ax = zoomxpos-game_area.x;
int ay = zoomypos-game_area.y;
// upper left corner of part
x = ax/32-1-x;
y = ay/32-1-y;
// ensure part is inside game_area
x = max(0, min(x, (game_area.w-part_width)/32-1));
y = max(0, min(y, (game_area.h-part_height)/32-1));
// adjust to game fields
x = x*32+24;
y = y*32+16;
break;
}
}
// Be sure to redraw everything, or actors may appear on top
// of the stones (actors are drawn in one pass and only
// clipped to the screen boundary).
display::RedrawAll(video::GetScreen());
// get the selected part from screen
// SDL_Surface *back = video::GetScreen()->get_surface();
Rect src_area(game_area.x+x, game_area.y+y, part_width, part_height);
Surface *src = Grab(video::GetScreen()->get_surface(), src_area);
// zoom multiple times for softer image
// const double stepsize = 0.3;
// for (double zoom = 0.4; zoom < 0.9; zoom += stepsize) {
// int sx = round_down<int>(zoom * vminfo->width);
// int sy = round_down<int>(zoom * vminfo->height);
// Surface *tmp = src->zoom(sx, sy);
// delete src;
// src = tmp;
// }
zoomed = src->zoom(vminfo->width, vminfo->height);
delete src;
}
ecl::blit(gc, 0,0, zoomed);
}
bool GameMenu::on_event (const SDL_Event &e)
{
if (e.type == SDL_MOUSEBUTTONDOWN
&& e.button.button == SDL_BUTTON_RIGHT)
{
Menu::quit();
return true;
}
return false;
}
void GameMenu::on_action(gui::Widget *w) {
lev::Index *ind = lev::Index::getCurrentIndex();
if (w == resume) {
Menu::quit();
}
else if (w == abort) {
client::Msg_Command("abort");
Menu::quit();
}
else if (w == restart) {
if (w->lastModifierKeys() & KMOD_CTRL && w->lastModifierKeys() & KMOD_SHIFT) {
// force a reload from file
lev::Proxy * curProxy = lev::Proxy::loadedLevel();
if (curProxy != NULL)
curProxy->release();
}
client::Stop ();
server::Msg_LoadLevel(ind->getCurrent(), false);
Menu::quit();
}
else if (w == options) {
enigma::gui::ShowOptionsMenu (0);
invalidate_all();
// Menu::quit();
}
else if (w == info) {
LevelInspector m(ind->getCurrent());
m.manage();
invalidate_all();
// Menu::quit();
}
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_GAMEMENU_HH_INCLUDED
#define GUI_GAMEMENU_HH_INCLUDED
#include "gui/Menu.hh"
namespace enigma { namespace gui {
/* -------------------- GameMenu -------------------- */
class GameMenu : public Menu {
public:
GameMenu(int zoomxpos_, int zoomypos_);
virtual ~GameMenu();
private:
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
void draw_background(ecl::GC &gc);
gui::Widget *resume, *restart, *options, *info, *abort;
ecl::Surface *zoomed;
int zoomxpos, zoomypos; // position to be zoomed
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2003,2004 Daniel Heck, Ralf Westram
*
* 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 "gui/HelpMenu.hh"
#include "enigma.hh"
#include "video.hh"
#include "nls.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- HelpMenu -------------------- */
HelpMenu::HelpMenu (const char **helptext_, int xoffset_) :
helptext (helptext_),
ok (new gui::StaticTextButton(N_("Ok"), this)),
cfg (xoffset_)
{
const video::VMInfo &vminfo = *video::GetInfo();
add(ok, Rect(vminfo.width-170,vminfo.height-60,150,40));
}
bool HelpMenu::on_event (const SDL_Event &e)
{
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT)
{
Menu::quit();
return true;
}
return false;
}
void HelpMenu::on_action (gui::Widget *w)
{
if (w == ok)
Menu::quit();
}
void HelpMenu::draw_background (ecl::GC &gc)
{
const video::VMInfo &vminfo = *video::GetInfo();
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont(cfg.fontname.c_str());
int y = cfg.y0 + (vminfo.height - 480)/2;
int x = (vminfo.width-640)/2;
for (int i = 0; helptext[i]; i += 2)
{
f->render (gc, cfg.x0 + x, y, _(helptext[i])); // translate
f->render (gc, cfg.x1 + x, y, _(helptext[i+1])); // translate
y += cfg.yskip;
}
}
/* -------------------- Functions -------------------- */
void displayHelp(const char **helptext, int xoffset)
{
FX_Fade (video::FADEOUT);
HelpMenu menu(helptext, xoffset);
menu.draw_all();
FX_Fade (video::FADEIN);
menu.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2002,2003 Daniel Heck, Ralf Westram
*
* 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 ENIGMA_HELP_HH
#define ENIGMA_HELP_HH
#include "gui/Menu.hh"
#include "ecl.hh"
namespace enigma { namespace gui {
struct HelpMenuConfig {
int x0, x1; // x coordinates of first and second column
int y0; // y coordinate
int yskip;
std::string fontname;
HelpMenuConfig (int xoffset) {
x0 = 40;
x1 = x0 + xoffset;
y0 = 40;
yskip = 30;
fontname = "menufont";
}
};
class HelpMenu : public Menu {
public:
HelpMenu (const char **helptext_, int xoffset);
private:
bool on_event (const SDL_Event &e);
void on_action (gui::Widget *w);
void draw_background (ecl::GC &gc);
const char **helptext;
gui::Widget *ok;
HelpMenuConfig cfg;
};
void displayHelp (const char **helptext, int xoffset);
}} // namespace enigma::gui
#endif // ENIGMA_HELP_HH

View File

@@ -0,0 +1,84 @@
/*
* 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 "gui/InfoMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "video.hh"
#include "nls.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
InfoMenu::InfoMenu(const char **infotext, int pages) : info (infotext),
curPage (0), numPages (pages) {
const video::VMInfo &vminfo = *video::GetInfo();
but_ok = new StaticTextButton(N_("Ok"), this);
pgup = new ImageButton("ic-up", "ic-up1", this);
pgdown = new ImageButton("ic-down", "ic-down1", this);
add(but_ok, Rect(vminfo.width-120, vminfo.height-60, 100, 40));
add(pgup, Rect(vminfo.width-30, vminfo.height/2, 20, 50));
add(pgdown, Rect(vminfo.width-30, vminfo.height/2 +70, 20, 50));
}
void InfoMenu::draw_background(ecl::GC &gc) {
const video::VMInfo &vminfo = *video::GetInfo();
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
int row = 0;
for (int p=0; p<curPage; p++) {
while (info[row])
row++;
// select first row of next page
row++;
}
for (int i = 0; info[row]; row++, i++) {
const char *t = _(info[row]);
f->render (gc, 40 + (vminfo.width-640)/2,
20 + (vminfo.height-480)/2 + i*f->get_height(), t);
}
}
void InfoMenu::on_action (gui::Widget *w) {
if (w == but_ok) {
Menu::quit();
} else if (w == pgup) {
if (curPage > 0) {
curPage--;
invalidate_all();
}
} else if (w == pgdown) {
if (curPage < numPages - 1) {
curPage++;
invalidate_all();
}
}
}
void displayInfo(const char **infotext, int pages) {
FX_Fade (video::FADEOUT);
InfoMenu menu(infotext, pages);
menu.draw_all();
FX_Fade (video::FADEIN);
menu.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,42 @@
/*
* 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 GUI_INFOMENU_HH_INCLUDED
#define GUI_INFOMENU_HH_INCLUDED
#include "gui/Menu.hh"
namespace enigma { namespace gui {
class InfoMenu : public gui::Menu {
public:
InfoMenu(const char **infotext, int pages);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
const char **info;
int curPage;
int numPages;
Widget *but_ok;
Widget *pgup;
Widget *pgdown;
};
void displayInfo(const char **helptext, int pages);
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,231 @@
/*
* 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 "gui/LPGroupConfig.hh"
#include "gui/LevelPackConfig.hh"
#include "ecl.hh"
#include "errors.hh"
#include "nls.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
LPGroupConfig::LPGroupConfig(std::string groupName) : oldGroupName (groupName) {
const video::VMInfo &vminfo = *video::GetInfo();
groups = lev::Index::getGroupNames();
position = -1;
for (int i = 0; i < groups.size(); i++) {
if (groups[i] == groupName) {
position = i;
break;
}
}
oldPosition = position;
if (position < 0) {
groups.push_back(groupName);
position = groups.size() - 1;
}
VList * titleVList = new VList;
titleVList->set_spacing(12);
titleVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
titleVList->set_default_size(160, 35);
Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT);
titleVList->add_back(groupLabel);
VList * groupsVList = new VList;
groupsVList->set_spacing(12);
groupsVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
groupsVList->set_default_size(160, 35);
Label * positionLabel = new Label(N_("Position:"), HALIGN_LEFT);
pre2Group = new UntranslatedLabel("");
pre1Group = new UntranslatedLabel("");
tf_groupName = new TextField(groupName);
post1Group = new UntranslatedLabel("");
post2Group = new UntranslatedLabel("");
Label * dummyLabel = new Label("");
groupsVList->add_back(positionLabel);
groupsVList->add_back(pre2Group);
groupsVList->add_back(pre1Group);
groupsVList->add_back(tf_groupName);
groupsVList->add_back(post1Group);
groupsVList->add_back(post2Group);
groupsVList->add_back(dummyLabel);
VList * scrollVList = new VList;
scrollVList->set_spacing(12);
scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
scrollVList->set_default_size(40, 35);
scrollUp = new ImageButton("ic-up", "ic-up1", this);
scrollDown = new ImageButton("ic-down", "ic-down1", this);
scrollVList->add_back(scrollUp);
scrollVList->add_back(scrollDown);
this->add(titleVList, Rect(vminfo.width/2 - 290, 0, 160, vminfo.height-100));
this->add(groupsVList, Rect(vminfo.width/2 - 80, 0, 160, vminfo.height-100));
this->add(scrollVList, Rect(vminfo.width/2 + 130, 0, 40, vminfo.height-100));
errorLabel = new Label("", HALIGN_CENTER);
this->add(errorLabel, Rect(10, vminfo.height-100, vminfo.width-20, 35));
// Create buttons - positioning identical to Levelmenu
but_newPack = new StaticTextButton(N_("New Pack"), this);
but_delete = new StaticTextButton(N_("Delete Group"), this);
but_ignore = new StaticTextButton(N_("Undo"), this);
but_ok = new StaticTextButton(N_("Ok"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(but_newPack);
commandHList->add_back(but_delete);
commandHList->add_back(but_ignore);
commandHList->add_back(but_ok);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
updateGroupList();
}
void LPGroupConfig::updateGroupList() {
pre2Group->set_text((position > 1) ? groups[position - 2] : "");
pre1Group->set_text((position > 0) ? groups[position - 1] : "");
post1Group->set_text((position < groups.size() - 1) ? groups[position + 1] : "");
post2Group->set_text((position < groups.size() - 2) ? groups[position + 2] : "");
}
bool LPGroupConfig::doChanges() {
// rename first for consistency
std::string newName = tf_groupName->getText();
std::string::size_type lastChar = newName.find_last_not_of(" ");
if (lastChar == std::string::npos) {
// the name is effectively an empty string
errorLabel->set_text(N_("Error: empty group name not allowed - press \"Undo\" to exit without modifications"));
return false;
}
// strip off trailing and leading spaces
newName = newName.substr(0 , lastChar + 1);
newName = newName.substr(newName.find_first_not_of(" "));
// check if new group name is unique
for (int i = 0; i < groups.size(); i++) {
if (i != position && groups[i] == newName) {
errorLabel->set_text(N_("Error: group name is a duplicate of an existing group"));
return false;
}
}
if (newName == INDEX_EVERY_GROUP) {
errorLabel->set_text(N_("Error: \"Every group\" is a reserved group name"));
return false;
}
if (newName.size() > 2 && newName[0] == '['
&& newName[newName.size() -1] == ']') {
errorLabel->set_text(N_("Error: group name must not be enclosed in square brackets"));
return false;
}
if (oldGroupName.empty()) {
// menu called without an existing group
lev::Index::insertGroup(newName, position);
} else {
// menu called with an existing group
if (newName != oldGroupName)
lev::Index::renameGroup(oldGroupName, newName);
if (oldPosition >= 0 && position != oldPosition) {
// move the group to the new position
lev::Index::moveGroup(newName, position);
}
}
return true;
}
void LPGroupConfig::on_action(Widget *w) {
if (w == but_ok) {
if (doChanges())
Menu::quit();
else
invalidate_all();
} else if (w == but_ignore) {
Menu::quit();
} else if (w == scrollUp) {
if (position > 0) {
std::string tmp = groups[position];
groups[position] = groups[position - 1];
groups[position - 1] = tmp;
position--;
updateGroupList();
invalidate_all();
}
} else if (w == scrollDown) {
if (position < groups.size() - 1) {
std::string tmp = groups[position];
groups[position] = groups[position + 1];
groups[position + 1] = tmp;
position++;
updateGroupList();
invalidate_all();
}
} else if (w == but_delete) {
std::vector<lev::Index *> * indices = lev::Index::getGroup(oldGroupName);
if (indices != NULL) {
// reassign remaining indices from back to front to keep the
// group vector valid
for (int i = indices->size() - 1; i >= 0; i--) {
if ((*indices)[i]->getGroupName() != INDEX_EVERY_GROUP) {
LevelPackConfig m((*indices)[i]->getName(), oldGroupName, true);
if (!m.manage() || m.isUndoQuit()) {
errorLabel->set_text(N_("Error: group not empty"));
invalidate_all();
return;
}
}
}
lev::Index::deleteGroup(oldGroupName);
}
Menu::quit();
} else if (w == but_newPack) {
if (doChanges()) {
LevelPackConfig m("");
m.manage();
Menu::quit();
} else {
invalidate_all();
}
}
}
void LPGroupConfig::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Group Configuration"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,56 @@
/*
* 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 GUI_LPGROUPCONFIG_HH_INCLUDED
#define GUI_LPGROUPCONFIG_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
namespace enigma { namespace gui {
class LPGroupConfig : public gui::Menu {
public:
LPGroupConfig (std::string groupName);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
void updateGroupList();
bool doChanges();
TextField *tf_groupName;
Label *pre2Group;
Label *pre1Group;
Label *post1Group;
Label *post2Group;
Widget *scrollUp;
Widget *scrollDown;
Label *errorLabel;
Widget *but_newPack;
Widget *but_delete;
Widget *but_ignore;
Widget *but_ok;
std::vector<std::string> groups;
int position; // new position of group that the user selected
int oldPosition; // position of group when entering menu, -1 for new group
std::string oldGroupName;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,712 @@
/*
* 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 "errors.hh"
#include "gui/LevelInspector.hh"
#include "main.hh"
#include "enigma.hh"
#include "nls.hh"
#include "video.hh"
#include "ecl_util.hh"
#include "gui/MonospacedLabel.hh"
#include "gui/LevelPreviewCache.hh"
#include "gui/ScreenshotViewer.hh"
#include "lev/RatingManager.hh"
#include "lev/ScoreManager.hh"
#include "StateManager.hh"
#include <vector>
using namespace ecl;
using namespace enigma;
namespace enigma { namespace gui {
class IntelligenceButton : public ValueButton {
int get_value() const {
return theRatingMgr->getIntelligence(theLevel);
}
void set_value(int value) {
theRatingMgr->setIntelligence(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
IntelligenceButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class DexterityButton : public ValueButton {
int get_value() const {
return theRatingMgr->getDexterity(theLevel);
}
void set_value(int value) {
theRatingMgr->setDexterity(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
DexterityButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class PatienceButton : public ValueButton {
int get_value() const {
return theRatingMgr->getPatience(theLevel);
}
void set_value(int value) {
theRatingMgr->setPatience(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
PatienceButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class KnowledgeButton : public ValueButton {
int get_value() const {
return theRatingMgr->getKnowledge(theLevel);
}
void set_value(int value) {
theRatingMgr->setKnowledge(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
KnowledgeButton(lev::Proxy *aLevel) : ValueButton(0, 6),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class SpeedButton : public ValueButton {
int get_value() const {
return theRatingMgr->getSpeed(theLevel);
}
void set_value(int value) {
theRatingMgr->setSpeed(theLevel, value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
SpeedButton(lev::Proxy *aLevel) : ValueButton(0, 5),
theLevel (aLevel) {
theRatingMgr = lev::RatingManager::instance();
init();
}
private:
lev::RatingManager *theRatingMgr;
lev::Proxy *theLevel;
};
class RatingButton : public ValueButton {
int get_value() const {
return theScoreMgr->getRating(theLevel);
}
void set_value(int value) {
theScoreMgr->setRating(theLevel, value);
if (get_parent() != NULL) {
get_parent()->invalidate_all();
}
}
string get_text(int value) const {
return (value == -1) ? "-" : ecl::strf("%d", value);
}
public:
RatingButton(lev::Proxy *aLevel) : ValueButton(-1, 10),
theLevel (aLevel) {
theScoreMgr = lev::ScoreManager::instance();
init();
}
private:
lev::ScoreManager *theScoreMgr;
lev::Proxy *theLevel;
};
LevelInspector::LevelInspector(lev::Proxy *aLevel, bool showDeveloperInfo):
levelProxy(aLevel), isDeveloperMode(showDeveloperInfo), annotation (new TextField()),
back (new StaticTextButton(N_("Ok"), this)),
screenshot (new StaticTextButton(N_("Screenshot"), this))
{
bool didGenerate; // dummy
previewImage = LevelPreviewCache::instance()->getPreview(aLevel, true, didGenerate);
const video::VMInfo *vminfo = video::GetInfo();
vspacing = vminfo->height < 500 ? 2 :(vminfo->height < 650 ? 3 : 4);
vspacing2 = vminfo->height < 500 ? 16 :(vminfo->height < 650 ? 14 : 16);
vmargin = vminfo->height < 500 ? 10 :(vminfo->height < 650 ? 20 : 30);
hmargin = vminfo->width < 660 ? 10 : (vminfo->width < 900 ? 20 : 30);
bool highres = vminfo->height > 650 ? true : false;
bool lowres = vminfo->height < 600 ? true : false;
add(back, Rect(vminfo->width-130-2*hmargin,vminfo->height-50,130,35));
add(screenshot, Rect(vminfo->width-260-3*hmargin,vminfo->height-50,130,35));
try {
aLevel->loadMetadata(true);
}
catch (XLevelLoading &err) {
std::vector<string> lines;
std::string errmsg = _("Server Error: could not load level '")
+ aLevel->getNormLevelPath() + "'\n"
+ err.what();
ecl::split_copy (errmsg, '\n', back_inserter(lines));
int x = 60;
int y = 60;
int yskip = 25;
for (unsigned i=0; i<lines.size(); ++i) {
add(new Label(lines[i], HALIGN_LEFT), Rect(x, y, vminfo->width-80,yskip));
y += yskip;
}
return;
}
std::string tmp, tmp2;
lev::RatingManager *theRatingMgr = lev::RatingManager::instance();
lev::ScoreManager *theScoreMgr = lev::ScoreManager::instance();
withEasy = aLevel->hasEasymode();
ratingInherited = theScoreMgr->isRatingInherited(aLevel);
ecl::Font *menufont = enigma::GetFont("menufont");
levelPathString =
(levelProxy->getNormPathType() == lev::Proxy::pt_resource) ?
levelProxy->getAbsLevelPath() : levelProxy->getNormLevelPath();
// substitute all backslashes by slashes
for (std::string::size_type slpos = levelPathString.find('\\');
slpos != std::string::npos; slpos = levelPathString.find('\\'))
levelPathString.replace(slpos, 1, 1, '/');
BuildVList authorT(this, Rect(hmargin,vmargin,110,25), vspacing);
authorT.add(new Label(N_("Level: "), HALIGN_RIGHT));
authorT.add(new Label((""), HALIGN_RIGHT)); // subtitle
authorT.add(new Label(N_("Author: "), HALIGN_RIGHT));
authorT.add(new Label(N_("Contact: "), HALIGN_RIGHT));
authorT.add(new Label(N_("Homepage: "), HALIGN_RIGHT));
BuildVList author(this, Rect(hmargin+110+10,vmargin,
vminfo->width-(hmargin+110+10)-10-(vminfo->thumbw+10)-hmargin,25), vspacing);
tmp = levelProxy->getTitle();
tmp2 = levelProxy->getLocalizedString("title");
if (tmp != tmp2)
tmp = tmp + " -- " + tmp2;
author.add(new Label( tmp, HALIGN_LEFT));
tmp = levelProxy->getLocalizedString("subtitle");
if (tmp == "subtitle")
tmp = "";
author.add(new Label(tmp, HALIGN_LEFT)); // subtitle
author.add(new Label(levelProxy->getAuthor(), HALIGN_LEFT));
BuildVList address(this, Rect(hmargin+110+10,vmargin+3*(25+vspacing),
vminfo->width-(hmargin+110+10)-hmargin,25), vspacing);
address.add(new Label(levelProxy->getContact(), HALIGN_LEFT));
address.add(new Label(levelProxy->getHomepage(), HALIGN_LEFT));
BuildVList ratingPubT(this, Rect(hmargin+65,vmargin+5*25+4*vspacing+vspacing2, 130,25), 2);
ratingPubT.add(new Label(N_("Public Ratings"), HALIGN_CENTER));
BuildVList ratingPubST(this, Rect(hmargin,vmargin+6*25+5*vspacing+vspacing2, 130,25), 2);
ratingPubST.add(new Label(N_("Intelligence: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Dexterity: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Patience: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Knowledge: "), HALIGN_RIGHT));
ratingPubST.add(new Label(N_("Speed: "), HALIGN_RIGHT));
if (!lowres) ratingPubST.add(new Label(N_("Difficulty: "), HALIGN_RIGHT));
BuildVList ratingPub(this, Rect(hmargin+130+15,vmargin+6*25+5*vspacing+vspacing2, 30,25), 2);
if (WizardMode) {
ratingPub.add(new IntelligenceButton(aLevel));
ratingPub.add(new DexterityButton(aLevel));
ratingPub.add(new PatienceButton(aLevel));
ratingPub.add(new KnowledgeButton(aLevel));
ratingPub.add(new SpeedButton(aLevel));
if (!lowres) ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDifficulty(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
} else {
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getIntelligence(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDexterity(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getPatience(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getKnowledge(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getSpeed(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
if (!lowres) ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDifficulty(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER));
}
BuildVList scoresT(this, Rect(vminfo->width/2-100-20,vmargin+5*25+4*vspacing+vspacing2,100,25), 2);
scoresT.add(new Label(N_("Scores"), HALIGN_RIGHT));
BuildVList scoresST(this, Rect(vminfo->width/2-100-20,vmargin+6*25+5*vspacing+vspacing2,100,25), 2);
scoresST.add(new Label(N_("You: "), HALIGN_RIGHT));
scoresST.add(new Label(N_("World: "), HALIGN_RIGHT));
// TRANSLATORS: PAR = professional average rate - an expression used by golfers
scoresST.add(new Label(N_("PAR: "), HALIGN_RIGHT));
scoresST.add(new Label(N_("Author: "), HALIGN_RIGHT));
scoresST.add(new Label(N_("Solved %: "), HALIGN_RIGHT));
if (!lowres) scoresST.add(new Label(N_("Solved #: "), HALIGN_RIGHT));
BuildVList scores(this, Rect(vminfo->width/2-15+(withEasy?0:20),
vmargin+6*25+5*vspacing+vspacing2,(withEasy?117:54),25), 2);
scores.add(new MonospacedLabel(scoreToString(theScoreMgr->getBestUserScore(aLevel, DIFFICULTY_EASY),
theScoreMgr->getBestUserScore(aLevel, DIFFICULTY_HARD),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(scoreToString(theRatingMgr->getBestScoreEasy(aLevel),
theRatingMgr->getBestScoreDifficult(aLevel),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(scoreToString(theRatingMgr->getParScoreEasy(aLevel),
theRatingMgr->getParScoreDifficult(aLevel),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(scoreToString(aLevel->getEasyScore(),
aLevel->getDifficultScore(),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER));
scores.add(new MonospacedLabel(withEasy ? (theRatingMgr->getPcSolvedEasy(aLevel) +
" /" + theRatingMgr->getPcSolvedDifficult(aLevel) + " ").c_str() :
(theRatingMgr->getPcSolvedDifficult(aLevel) + " ").c_str(),
'8', " 0123456789", HALIGN_CENTER));
if (!lowres) scores.add(new MonospacedLabel(withEasy ? (ecl::strf("%5d", theRatingMgr->getNumSolvedEasy(aLevel)) +
" /" + ecl::strf("%5d", theRatingMgr->getNumSolvedDifficult(aLevel)) + " ").c_str() :
(ecl::strf("%5d", theRatingMgr->getNumSolvedDifficult(aLevel)) + " ").c_str(),
'8', " 0123456789", HALIGN_CENTER));
BuildVList versionT(this, Rect(vminfo->width-100/2-90-2*hmargin,vmargin+5*25+4*vspacing+vspacing2,100,25), 2);
versionT.add(new Label(N_("Version"), HALIGN_CENTER));
BuildVList versionST(this, Rect(vminfo->width-110-90-2*hmargin,vmargin+6*25+5*vspacing+vspacing2,110,25), 2);
if (!lowres || aLevel->getLevelStatus() == lev::STATUS_RELEASED)
versionST.add(new Label(N_("Score: "), HALIGN_RIGHT));
else
versionST.add(new Label(N_("Status: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Release: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Revision: "), HALIGN_RIGHT));
if (!lowres)
versionST.add(new Label(N_("Status: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Control: "), HALIGN_RIGHT));
versionST.add(new Label(N_("Target: "), HALIGN_RIGHT));
BuildVList version(this, Rect(vminfo->width-80-2*hmargin,vmargin+6*25+5*vspacing+vspacing2,80+2*hmargin,25), 2);
if (!lowres || aLevel->getLevelStatus() == lev::STATUS_RELEASED)
version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getScoreVersion()).c_str(),
'8', " 0123456789", HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_STABLE)
version.add(new Label(N_("stable"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_TEST)
version.add(new Label(N_("test"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_EXPERIMENTAL)
version.add(new Label(N_("experimental"), HALIGN_LEFT));
else
version.add(new Label(N_("unknown"), HALIGN_LEFT));
version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getReleaseVersion()).c_str(),
'8', " 0123456789", HALIGN_LEFT));
version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getRevisionNumber()).c_str(),
'8', " 0123456789", HALIGN_LEFT));
if (!lowres)
if (aLevel->getLevelStatus() == lev::STATUS_RELEASED)
version.add(new Label(N_("released"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_STABLE)
version.add(new Label(N_("stable"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_TEST)
version.add(new Label(N_("test"), HALIGN_LEFT));
else if (aLevel->getLevelStatus() == lev::STATUS_EXPERIMENTAL)
version.add(new Label(N_("experimental"), HALIGN_LEFT));
else
version.add(new Label(N_("unknown"), HALIGN_LEFT));
switch (aLevel->getControl()) {
case lev::force:
version.add(new Label(N_("force"), HALIGN_LEFT));
break;
case lev::balance:
version.add(new Label(N_("balance"), HALIGN_LEFT));
break;
case lev::key:
version.add(new Label(N_("key"), HALIGN_LEFT));
break;
default:
version.add(new Label(N_("unknown"), HALIGN_LEFT));
break;
}
#if 0
// fake gettext to register the following strings for I18N
_("time")
_("pushes")
_("moves")
#endif
version.add(new Label(aLevel->getScoreTarget().c_str(), HALIGN_LEFT));
int bestScoreHolderLines = 0;
int creditsLines = 0;
int dedicationLines = 0;
int levelPathLines = 0;
int annotationLines = 0;
int compatibilityLines = 0;
int idLines = 0;
int vnext = vmargin+ (lowres?11:12)*25+(lowres?9:10)*vspacing+2*vspacing2;
int textwidth = vminfo->width-3*hmargin-110-10;
dispatchBottomLines(bestScoreHolderLines, creditsLines, dedicationLines,
levelPathLines, annotationLines, compatibilityLines, idLines,
(vminfo->height-vnext-vmargin-25-vspacing2)/27, textwidth);
if (bestScoreHolderLines == 1) {
add(new Label(N_("World Record Holders: "), HALIGN_RIGHT),Rect(hmargin,vnext,200,25));
std::string holders;
if (withEasy) {
holders = theRatingMgr->getBestScoreEasyHolder(aLevel);
if (holders.empty())
holders = " - ";
holders += " / ";
}
if (theRatingMgr->getBestScoreDifficultHolder(aLevel).empty())
holders += " -";
else
holders += theRatingMgr->getBestScoreDifficultHolder(aLevel);
Label *wrLabel = new Label(holders, HALIGN_LEFT);
add(wrLabel, Rect(hmargin+200+10,vnext,textwidth-90,25));
if (!wrLabel->text_fits()) {
int cutEasy = 0;
int cutDiff = 0;
std::string diffHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel);
if (withEasy) {
std::string easyHolders = theRatingMgr->getBestScoreEasyHolder(aLevel);
bool hasEasyHolders = !easyHolders.empty();
bool hasDiffHolders = !diffHolders.empty();
int limit = 10;
do {
std::string cutHolders;
wrLabel->set_text(easyHolders);
if (!wrLabel->text_fits(0.48)) {
cutHolders = theRatingMgr->getBestScoreEasyHolder(aLevel, ++cutEasy);
if (cutHolders.empty())
cutEasy--;
else
easyHolders = cutHolders;
}
wrLabel->set_text(diffHolders);
if (!wrLabel->text_fits(0.48)) {
cutHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel, ++cutDiff);
if (cutHolders.empty())
cutDiff--;
else
diffHolders = cutHolders;
}
holders = (hasEasyHolders ? easyHolders : std::string(" - "))
+ " / " + (hasDiffHolders ? diffHolders : std::string(" -"));
wrLabel->set_text(holders);
limit--;
} while (!wrLabel->text_fits() && limit > 0);
} else {
std::string cutHolders;
do {
cutHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel, ++cutDiff);
wrLabel->set_text(cutHolders);
} while (!wrLabel->text_fits());
if (cutHolders.empty()) {
// we did cut off to many holders, take last attempt even if it was too long
wrLabel->set_text(theRatingMgr->getBestScoreDifficultHolder(aLevel, --cutDiff));
}
}
}
vnext += 25 + vspacing;
}
if (creditsLines >= 1) {
add(new Label(N_("Credits: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string creditsString = levelProxy->getCredits(true);
for (int i = 0; i< creditsLines; i++) {
std::string::size_type breakPos = breakString(menufont, creditsString,
" ", textwidth);
add(new Label(creditsString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
creditsString = creditsString.substr(breakPos);
vnext += (25 + vspacing);
}
}
if (dedicationLines >= 1) {
add(new Label(N_("Dedication: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string dedicationString = levelProxy->getDedication(true);
for (int i = 0; i< dedicationLines; i++) {
std::string::size_type breakPos = breakString( menufont, dedicationString,
" ", textwidth);
add(new Label(dedicationString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
dedicationString = dedicationString.substr(breakPos);
vnext += (25 + vspacing);
}
}
if (levelPathLines >= 1) {
add(new Label(N_("Level Path: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string workString = levelPathString;
for (int i = 0; i< levelPathLines - 1; i++) {
std::string::size_type breakPos = breakString(menufont, workString,
"/", textwidth);
add(new Label(workString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
workString = workString.substr(breakPos);
vnext += (25 + vspacing);
}
// show as much as possible from last line
if (menufont->get_width(workString.c_str()) > textwidth) {
// show the filename at the end - skip leading parts if necessary
add(new Label(workString, HALIGN_RIGHT), Rect(hmargin+110+10,vnext,textwidth,25));
} else {
// display up to the last character
add(new Label(workString, HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25));
}
vnext += (25 + vspacing);
}
if (idLines >= 1) {
add(new Label(N_("Id: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
add(new Label(levelProxy->getId(), HALIGN_LEFT),Rect(hmargin+110+10, vnext, textwidth, 25));
vnext += (25 + vspacing)*idLines;
}
if (compatibilityLines >= 1) {
add(new Label(N_("Compatibility: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
std::string compString = ecl::strf("Enigma v%.2f / ", levelProxy->getEnigmaCompatibility()) +
GetGameTypeName(levelProxy->getEngineCompatibility());
add(new Label(compString , HALIGN_LEFT),Rect(hmargin+110+10, vnext, textwidth, 25));
vnext += (25 + vspacing)*compatibilityLines;
}
annotation->set_text(app.state->getAnnotation(levelProxy->getId())); // field needs to initialized for saves
if (annotationLines >= 1) {
add(new Label(N_("Annotation: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
add(annotation, Rect(hmargin+110+10, vnext, textwidth, 25));
vnext += (25 + vspacing)*annotationLines;
}
vnext += vspacing2 - vspacing;
add(new Label(N_("Rating: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25));
add(new RatingButton(aLevel),Rect(hmargin+110+10,vnext,40,25));
add(new Label(N_("Average: "), HALIGN_RIGHT),Rect(hmargin+110+10+40+20,vnext,105,25));
add(new Label(theRatingMgr->getAverageRating(aLevel), HALIGN_RIGHT),Rect(hmargin+110+10+40+18+105+6,vnext,31,25));
}
LevelInspector::~LevelInspector () {
}
bool LevelInspector::isEndDeveloperMode() {
return isDeveloperMode;
}
bool LevelInspector::on_event (const SDL_Event &e) {
bool handled = false;
if (e.type == SDL_KEYDOWN) {
handled=true;
switch (e.key.keysym.sym) {
case SDLK_F2:
if (!isDeveloperMode) {
if (!annotation->getText().empty() ||
!app.state->getAnnotation(levelProxy->getId()).empty()) {
app.state->setAnnotation(levelProxy->getId(), annotation->getText());
}
LevelInspector m(levelProxy, true);
m.manage();
if (m.isEndDeveloperMode()) {
// reinit user input fields
annotation->set_text(app.state->getAnnotation(levelProxy->getId()));
invalidate_all();
} else {
Menu::quit();
}
} else {
if (!annotation->getText().empty() ||
!app.state->getAnnotation(levelProxy->getId()).empty()) {
app.state->setAnnotation(levelProxy->getId(), annotation->getText());
}
Menu::quit();
}
break;
default: handled=false; break;
}
}
return handled;
}
void LevelInspector::on_action(gui::Widget *w) {
if (w == back) {
// save annotation - but avoid to save unnecessary empty annotations
if (!annotation->getText().empty() ||
!app.state->getAnnotation(levelProxy->getId()).empty()) {
app.state->setAnnotation(levelProxy->getId(), annotation->getText());
}
isDeveloperMode = false;
Menu::quit();
} else if (w == screenshot) {
ScreenshotViewer m(levelProxy);
m.manage();
invalidate_all();
}
}
void LevelInspector::draw_background(ecl::GC &gc) {
const video::VMInfo *vminfo = video::GetInfo();
video::SetCaption((std::string("Enigma - Level ") +
(isDeveloperMode ? "Developer " : "") + "Inspector").c_str());
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
blit(gc, vminfo->width-vminfo->thumbw-10-hmargin, vmargin, previewImage);
Surface *img_hard = enigma::GetImage("completed");
if (withEasy) {
Surface *img_easy = enigma::GetImage("completed-easy");
blit (gc, vminfo->width/2-4, vmargin+5*25+4*vspacing+vspacing2, img_easy);
blit (gc, vminfo->width/2-4+63, vmargin+5*25+4*vspacing+vspacing2, img_hard);
} else {
blit (gc, vminfo->width/2-4+20, vmargin+5*25+4*vspacing+vspacing2, img_hard);
}
Surface *img_changed = enigma::GetImage("changed");
ratingInherited = lev::ScoreManager::instance()->isRatingInherited(levelProxy);
if (ratingInherited) {
int numLines = vminfo->height < 500 ? 14 :(vminfo->height < 650 ? 18 : 19);
blit (gc, hmargin+110+10+40, vmargin + numLines*25 +
(numLines-3)*vspacing + 3*vspacing2, img_changed);
}
}
void LevelInspector::tick(double dtime) {
}
std::string LevelInspector::ratingToString(int value) {
if (value == 0) {
// no rating available
return " ";
} else {
return ecl::strf("%3d", value);
}
}
std::string LevelInspector::scoreToString(int easy, int difficult,
lev::Proxy *aLevel, bool constLengthForCenteredClipping) {
if (withEasy) {
if (!constLengthForCenteredClipping)
return scoreToString(easy, aLevel) + " / " +
scoreToString(difficult, aLevel);
else
if (aLevel->getScoreUnit() == lev::duration)
//
return (easy >= 0 ? "- " : ": ") +
scoreToString(easy, aLevel) + " / " +
scoreToString(difficult, aLevel) +
(difficult >= 0 ? " -" : " :") ;
else
return (easy >= 0 ? "- " : " ") +
scoreToString(easy, aLevel) + " / " +
scoreToString(difficult, aLevel) +
(difficult >= 0 ? " -" : " ") ;
} else {
return scoreToString(difficult, aLevel);
}
}
std::string LevelInspector::scoreToString(int score, lev::Proxy *aLevel) {
if (aLevel->getScoreUnit() == lev::duration)
if (score >= 0 && score <= (99*60+59))
return ecl::strf("%2d:%02d", score/60, score%60);
else
return " - ";
else
if (score >= 0 && score <= 9999)
return ecl::strf("%4d", score);
else
return " -";
}
void LevelInspector::dispatchBottomLines(int &bestScoreHolderLines,
int &creditsLines, int &dedicationLines, int &levelPathLines,
int &annotationLines, int &compatibilityLines, int &idLines, int numLines, int width) {
enum botType {holder, credits, dedication, path, annotation, compatibility, id};
const int sequenceSize = 13;
botType sequence1[sequenceSize] = {credits, dedication, annotation, path,
holder, annotation, path, compatibility, credits, dedication,
annotation, credits, annotation};
botType sequence2[sequenceSize] = {id, path, compatibility, holder, path,
annotation, annotation, credits, dedication,
credits, dedication, annotation, annotation};
botType *sequence = isDeveloperMode ? sequence2 : sequence1;
int j = 0;
std::string creditsString = levelProxy->getCredits(true);
std::string dedicationString = levelProxy->getDedication(true);
std::string pathWorkString = levelPathString;
ecl::Font *menufont = enigma::GetFont("menufont");
for (int i = 0; i<numLines; i++) {
bool assigned = false;
do {
switch (sequence[j++]) {
case holder:
bestScoreHolderLines++;
assigned = true;
break;
case credits:
if (!(creditsString.empty())) {
creditsLines++;
creditsString = creditsString.substr(breakString(menufont,
creditsString, " ", width));
assigned = true;
}
break;
case dedication:
if (!(dedicationString.empty())) {
dedicationLines++;
dedicationString = dedicationString.substr(breakString(menufont,
dedicationString, " ", width));
assigned = true;
}
break;
case path:
if (!(pathWorkString.empty())) {
levelPathLines++;
pathWorkString = pathWorkString.substr(breakString(menufont,
pathWorkString, "/", width));
assigned = true;
}
break;
case annotation:
annotationLines++;
assigned = true;
break;
case compatibility:
compatibilityLines++;
assigned = true;
break;
case id:
idLines++;
assigned = true;
break;
}
} while (!assigned && j < sequenceSize);
if (j == sequenceSize)
return;
}
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,68 @@
/*
* 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 GUI_LEVELINSPECTOR_HH_INCLUDED
#define GUI_LEVELINSPECTOR_HH_INCLUDED
#include "ecl.hh"
#include "gui/Menu.hh"
#include "gui/TextField.hh"
#include "lev/Proxy.hh"
namespace enigma { namespace gui {
class LevelInspector : public gui::Menu {
public:
LevelInspector (lev::Proxy *aLevel, bool showDeveloperInfo = false);
~LevelInspector ();
bool isEndDeveloperMode();
private:
// ActionListener interface.
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
// Menu interface.
void draw_background(ecl::GC &gc);
void tick(double dtime);
//
std::string ratingToString(int value);
std::string scoreToString(int easy, int difficult, lev::Proxy *aLevel,
bool constLengthForCenteredClipping);
std::string scoreToString(int score, lev::Proxy *aLevel);
void dispatchBottomLines(int &bestScoreHolderLines,
int &creditsLines, int &dedicationLines, int &levelPathLines,
int &annotationLines, int &compatibilityLines, int &idLines, int numLines, int width);
// Variables.
gui::Widget *back;
gui::Widget *screenshot;
gui::TextField *annotation;
ecl::Surface *previewImage;
lev::Proxy *levelProxy;
std::string levelPathString;
int vspacing;
int vspacing2;
int vmargin;
int hmargin;
bool withEasy;
bool ratingInherited;
bool isDeveloperMode;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,499 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 "gui/LevelMenu.hh"
#include "gui/HelpMenu.hh"
#include "gui/LevelPackMenu.hh"
#include "ecl.hh"
#include "game.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "server.hh"
#include "sound.hh"
#include "StateManager.hh"
#include "video.hh"
#include "lev/Index.hh"
using namespace std;
using namespace ecl;
namespace enigma { namespace gui {
/* -------------------- Level Menu -------------------- */
struct LevelMenuConfig {
int buttonw, ibuttonw, buttonh;
int lbuttonw, lbuttonh;
ecl::Rect previewarea;
int thumbsy; // y coordinate of thumbnail window
int leftborder;
LevelMenuConfig (const ecl::Rect &screen)
: buttonw (140), ibuttonw (90), buttonh (35),
lbuttonw (140), lbuttonh (100),
previewarea (10, 60, screen.w-50, screen.h-130),
thumbsy (60),
leftborder (10)
{}
};
LevelMenu::LevelMenu()
: but_advancemode (new AdvanceModeButton),
but_next (new ImageButton("ic-next", "ic-next1", this)),
but_back (new StaticTextButton(N_("Main Menu"), this)),
but_difficulty (new DifficultyButton),
but_levelpack (new StaticTextButton(N_("Level Pack"), this)),
lbl_lpinfo (new Label("")),
lbl_statistics (new Label("")),
lbl_levelname (new Label("", HALIGN_LEFT)),
lbl_levelinfo (new Label("", HALIGN_LEFT)),
shown_text_ttl(-1.0), main_quit (false)
{
HList *hl, *hll, *hlr ;
const video::VMInfo &vminfo = *video::GetInfo();
// Levelmenu configuration
const int Y2 = 10; // y position for information area
const int Y3 = vminfo.height-50; // y position for bottom button row
LevelMenuConfig c (Rect (0, 0, vminfo.width, vminfo.height));
but_difficulty->set_listener (this);
// Create buttons
hll = new HList;
hll->set_spacing (10);
hll->set_alignment (HALIGN_CENTER, VALIGN_TOP);
hll->set_default_size (c.ibuttonw, c.buttonh);
hll->add_back (but_advancemode);
hll->add_back (but_next);
hll->add_back (but_difficulty);
hlr = new HList;
hlr->set_spacing (10);
hlr->set_alignment (HALIGN_CENTER, VALIGN_TOP);
hlr->set_default_size (c.buttonw, c.buttonh);
hlr->add_back (but_levelpack);
hlr->add_back (but_back);
hl = new HList;
hl->set_spacing (10);
hl->set_alignment (HALIGN_CENTER, VALIGN_TOP);
hl->set_default_size (2*c.buttonw + 10, c.buttonh);
hl->add_back (hll);
hl->add_back (hlr);
this->add (hl, Rect(c.leftborder, Y3, vminfo.width-20, c.buttonh));
// Add navigation buttons
pgup = new ImageButton("ic-up", "ic-up1", this);
pgdown = new ImageButton("ic-down", "ic-down1", this);
start = new ImageButton("ic-top", "ic-top1", this);
end = new ImageButton("ic-bottom", "ic-bottom1", this);
Rect r(vminfo.width-30, c.thumbsy, 20, 50);
r.y = c.thumbsy;
add (pgup, r);
r.y += 60;
add (pgdown, r);
r.y = c.thumbsy + 240;
add (start, r);
r.y += 60;
add (end, r);
// Information area
hl = new HList;
hl->add_back (lbl_levelname, List::EXPAND);
hl->add_back (lbl_lpinfo, List::TIGHT);
this->add (hl, Rect (5, Y2, vminfo.width - 10, 28));
hl_info_stat = new HList;
hl_info_stat->add_back (lbl_levelinfo, List::EXPAND); //Rect (c.leftborder, Y2+20,305, 28));
hl_info_stat->add_back (lbl_statistics, List::TIGHT);
this->add (hl_info_stat, Rect (5, Y2+20, vminfo.width - 10, 28));
// Prepare level selection widget
levelwidget = new LevelWidget();
levelwidget->set_listener(this);
levelwidget->realize (c.previewarea);
levelwidget->set_area (c.previewarea);
this->add (levelwidget);
updateIndex();
}
void LevelMenu::tick(double dtime)
{
levelwidget->tick(dtime);
static double timeaccu = 0.0;
// info texts disappear after some time
if (shown_text_ttl>0.0) {
shown_text_ttl -= dtime;
if (shown_text_ttl <= 0.0)
shown_text = "";
}
timeaccu += dtime;
if (timeaccu > 0.1) {
update_info();
timeaccu = 0.0;
}
}
static const char *helptext_levelmenu[] = {
N_("Escape:"), N_("Skip to main menu"),
"F1:", N_("Show this help"),
"F5:", 0, // see below
N_("Arrows:"), N_("Select level"),
N_("Return:"), N_("Play selected level"),
N_("Back/Space:"), N_("Previous/next levelpack"),
"u", N_("Mark current level as Unsolved"),
// "s", N_("Mark current level as Solved"),
N_("Alt+Return:"), N_("Switch between fullscreen and window"),
N_("Left click:"), N_("Play selected level"),
N_("Right or control click:"), N_("Inspect selected level"),
0
};
bool LevelMenu::on_event (const SDL_Event &e)
{
// Pass all events to the level widget first
bool handled=levelwidget->on_event(e);
if (!handled) {
if (e.type == SDL_KEYDOWN) {
handled=true;
switch (e.key.keysym.sym) {
case SDLK_SPACE: next_levelpack(); break;
case SDLK_BACKSPACE: previous_levelpack(); break;
case SDLK_F1:
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST)
helptext_levelmenu[5] = N_("Select next level for world record hunt");
else
helptext_levelmenu[5] = N_("Select next unsolved level");
displayHelp(helptext_levelmenu, 200);
draw_all();
break;
case SDLK_F5:
next_unsolved();
break;
case SDLK_u: {
lev::ScoreManager::instance()->markUnsolved(lev::Index::getCurrentProxy(),
app.state->getInt("Difficulty"));
invalidate_all();
break;
}
case SDLK_s:
lev::ScoreManager::instance()->markSolved(lev::Index::getCurrentProxy(),
app.state->getInt("Difficulty"));
invalidate_all();
break;
default: handled=false; break;
}
}
else
handled = Menu::on_event (e);
}
return handled;
}
void LevelMenu::on_action(Widget *w)
{
if (w==levelwidget) {
lev::Index *ind = lev::Index::getCurrentIndex();
int ilevel = ind->getCurrentPosition();
if (w->lastModifierKeys() & KMOD_CTRL && w->lastModifierKeys() & KMOD_SHIFT) {
// force a reload from file
lev::Proxy * curProxy = lev::Proxy::loadedLevel();
if (curProxy != NULL)
curProxy->release();
}
if ((unsigned)ilevel < ind->size()) {
if (ind->mayPlayLevel(ilevel+1)) {
game::StartGame();
ilevel = ind->getCurrentPosition();
invalidate_all();
ind->setCurrentPosition(ilevel);
levelwidget->syncFromIndexMgr();
}
else
show_text(_("You are not allowed to play this level yet."));
}
} else if (w == but_back) {
main_quit = true;
Menu::quit();
} else if (w == pgup) {
levelwidget->page_up();
} else if (w == pgdown) {
levelwidget->page_down();
} else if (w == start) {
levelwidget->start();
} else if (w == end) {
levelwidget->end();
} else if (w == but_next) {
next_unsolved();
} else if (w == but_levelpack) {
main_quit = false;
Menu::quit();
} else if (w == but_difficulty) {
but_difficulty->on_action(w);
invalidate_all();
}
}
void LevelMenu::update_info() {
// Note: all format strings have to be translated directly
// as the formatted strings can no longer be translated.
// The instant language change is guaranteed by the frequent
// call of is method!
lev::Index *ind = lev::Index::getCurrentIndex();
int size = ind->size();
lev::ScoreManager *scm = lev::ScoreManager::instance();
lev::Proxy *curProxy = ind->getCurrent();
int difficulty = app.state->getInt("Difficulty");
lbl_lpinfo->set_text(ecl::strf(_("%s: %d levels"),
ind->getName().c_str(), size));
if (size == 0) {
// empty level pack
lbl_statistics->set_text ("-");
lbl_levelname->set_text ("-");
lbl_levelinfo->set_text ("-");
}
else {
int iselected = ind->getCurrentPosition();
// Display levelpack statistics (percentage of solved levels)
if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) {
int pct = 100* scm->countBestScore(ind, difficulty)/ size;
lbl_statistics->set_text(ecl::strf(_("%d%% best"), pct));
}
else if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_OVER_PAR) {
int pct = 100* scm->countParScore(ind, difficulty)/ size;
double hcp = scm->calcHCP(ind, difficulty);
lbl_statistics->set_text(ecl::strf(_("%d%% par, hcp %.1f"), pct, hcp));
}
else {
int pct = 100* scm->countSolved(ind, difficulty) / size;
lbl_statistics->set_text(ecl::strf(_("%d%% solved"), pct));
}
// Display level name
if (enigma::WizardMode) {
// add level path info - we just can display the normalized path
// as we did not yet locate the absolute path - the user can
// use the inspector to check the absolute path!
lbl_levelname->set_text(ecl::strf("#%d: %s (%s)",
ind->getCurrentLevel(), curProxy->getTitle().c_str(),
curProxy->getNormLevelPath().c_str()));
} else {
lbl_levelname->set_text(ecl::strf("#%d: %s",
ind->getCurrentLevel(), curProxy->getTitle().c_str()));
}
// Display best time
if (shown_text.length()) {
lbl_levelinfo->set_text(shown_text);
}
else {
// TODO prepare for scores that are not time based!
char txt[200];
lev::RatingManager *ratingMgr = lev::RatingManager::instance();
int wr_time = ratingMgr->getBestScore(curProxy, difficulty);
int par_time = ratingMgr->getParScore(curProxy, difficulty);
bool is_par = scm->parScoreReached(curProxy, difficulty);
int best_user_time = scm->getBestUserScore(curProxy, difficulty);
string wr_name = ratingMgr->getBestScoreHolder(curProxy, difficulty);
bool wr_name_displayed = false;
string your_time;
string wr_text;
if (best_user_time>0) {
your_time = strf(_("Your time: %d:%02d"),
best_user_time/60, best_user_time%60);
if (wr_time>0) {
int below = wr_time - best_user_time;
if (below == 0)
wr_text = _("That's world record.");
else if (below>0)
wr_text = strf(_("That's %d:%02d below world record."),
below/60, below%60);
}
}
if (wr_text.length() == 0 && wr_time>0) {
if (wr_name.length()) {
wr_name_displayed = true;
} else
if (is_par || par_time < 0)
wr_text = strf(_("World record: %d:%02d"), wr_time/60, wr_time%60);
else
wr_text = strf(_("Par: %d:%02d World record: %d:%02d"),
par_time/60, par_time%60, wr_time/60, wr_time%60);
}
if (!your_time.empty())
your_time += " ";
int wr_cut = 0;
do {
if (wr_name_displayed) {
std::string tmp = ratingMgr->getBestScoreHolder(curProxy, difficulty, wr_cut++);
if (!tmp.empty())
wr_name = tmp;
if (is_par || par_time < 0)
wr_text = strf(_("World record by %s: %d:%02d"),
wr_name.c_str(), wr_time/60, wr_time%60);
else
wr_text = strf(_("Par: %d:%02d World record by %s: %d:%02d"),
par_time/60, par_time%60, wr_name.c_str(), wr_time/60, wr_time%60);
}
lbl_levelinfo->set_text(your_time + wr_text);
} while (!hl_info_stat->fits() && wr_name_displayed && (wr_cut < 20));
}
}
}
void LevelMenu::updateIndex()
{
levelwidget->syncFromIndexMgr();
update_info();
}
void LevelMenu::draw_background(ecl::GC &gc)
{
video::SetCaption(("Enigma - Level Menu"));
sound::PlayMusic (options::GetString("MenuMusicFile"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
void LevelMenu::next_unsolved()
{
lev::Index *ind = lev::Index::getCurrentIndex();
if (ind->advanceLevel(lev::ADVANCE_NEXT_MODE)) {
levelwidget->syncFromIndexMgr();
} else
show_text(_("No further unsolved level available!"));
}
void LevelMenu::next_levelpack()
{
lev::Index::setCurrentIndex(lev::Index::nextGroupIndex()->getName());
updateIndex();
}
void LevelMenu::previous_levelpack() {
lev::Index::setCurrentIndex(lev::Index::previousGroupIndex()->getName());
updateIndex();
}
void LevelMenu::show_text(const string& text) {
shown_text = text;
shown_text_ttl = 2.0; // show for two seconds
}
bool LevelMenu::isMainQuit() {
return main_quit;
}
/* -------------------- DifficultyButton -------------------- */
DifficultyButton::DifficultyButton() : ImageButton("ic-easymode","ic-easymode",this) {
update();
}
void DifficultyButton::update() {
if (app.state->getInt("Difficulty") == DIFFICULTY_EASY)
ImageButton::set_images("ic-easymode","ic-normalmode");
else
ImageButton::set_images("ic-normalmode","ic-easymode");
}
void DifficultyButton::on_action(Widget *)
{
int newdifficulty = (DIFFICULTY_EASY+DIFFICULTY_HARD) - app.state->getInt("Difficulty");
app.state->setProperty("Difficulty", newdifficulty); update();
invalidate();
}
void DifficultyButton::draw(ecl::GC &gc, const ecl::Rect &r) {
update();
ImageButton::draw(gc, r);
}
/* -------------------- AdvanceModeButton -------------------- */
AdvanceModeButton::AdvanceModeButton() : ImageButton("","",this) {
update();
}
void AdvanceModeButton::update() {
switch (app.state->getInt("NextLevelMode")) {
case lev::NEXT_LEVEL_UNSOLVED :
ImageButton::set_images("ic-unsolved", "par");
break;
case lev::NEXT_LEVEL_OVER_PAR :
ImageButton::set_images("par", "ic-worldrecord");
break;
case lev::NEXT_LEVEL_NOT_BEST :
ImageButton::set_images("ic-worldrecord", "ic-strictlynext");
break;
case lev::NEXT_LEVEL_STRICTLY : // use as default, too
default:
ImageButton::set_images("ic-strictlynext","ic-unsolved");
break;
}
}
void AdvanceModeButton::on_action(Widget *)
{
switch (app.state->getInt("NextLevelMode")) {
case lev::NEXT_LEVEL_STRICTLY :
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_UNSOLVED);
break;
case lev::NEXT_LEVEL_UNSOLVED :
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_OVER_PAR);
break;
case lev::NEXT_LEVEL_OVER_PAR :
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_NOT_BEST);
break;
case lev::NEXT_LEVEL_NOT_BEST :
default:
app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_STRICTLY);
}
update();
invalidate();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_LEVELMENU_HH_INCLUDED
#define GUI_LEVELMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/LevelWidget.hh"
#include "lev/Proxy.hh"
#include "lev/Index.hh"
namespace enigma { namespace gui {
/* -------------------- LevelMenu -------------------- */
class LevelMenu : public Menu {
public:
LevelMenu();
// Rotate through levels and packs
void next_levelpack();
void previous_levelpack();
void show_text(const string& text);
bool isMainQuit();
private:
void update_info();
void next_unsolved();
void updateIndex();
// Menu interface.
void tick (double time);
void draw_background(ecl::GC &gc);
// Widget interface.
bool on_event (const SDL_Event &e);
// ActionListener interface.
void on_action(Widget *w);
// Variables.
Widget *pgup, *pgdown, *start, *end;
Widget *but_advancemode; // Next unsolved level button
Widget *but_next;
Widget *but_back; // "Back" button
Widget *but_difficulty; // "Difficulty" button
TextButton *but_levelpack; // "Levelpack" button
HList *hl_info_stat;
Label *lbl_lpinfo; // Levelpack information
Label *lbl_statistics; // percentage solved
Label *lbl_levelname;
Label *lbl_levelinfo;
LevelWidget *levelwidget;
string shown_text; // info text (disappears automatically)
double shown_text_ttl; // rest duration for shown_text
bool main_quit;
};
/* -------------------- Buttons -------------------- */
class DifficultyButton : public ImageButton {
// ActionListener interface.
void on_action(Widget *);
public:
DifficultyButton();
virtual void draw(ecl::GC &gc, const ecl::Rect &r);
private:
void update();
};
class AdvanceModeButton : public ImageButton {
// ActionListener interface.
void on_action(Widget *);
public:
AdvanceModeButton();
private:
void update();
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,391 @@
/*
* 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 "gui/LevelPackComposer.hh"
#include "gui/HelpMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "errors.hh"
#include "nls.hh"
#include "sound.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "main.hh"
#include <cstdio>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
lev::PersistentIndex * LevelPackComposer::clipboard = NULL;
static const char *helptext[] = {
N_("Shift click:"), N_("Add to clipboard"),
N_("Shift delete:"), N_("Clear clipboard"),
N_("F8:"), N_("Insert clipboard as reference"),
N_("F9:"), N_("Insert clipboard as copy"),
// N_("F10:"), N_("Move clipboard levels"),
N_("Alt left arrow:"), N_("Exchange level with predecessor"),
N_("Alt right arrow:"), N_("Exchange level with successor"),
N_("Delete:"), N_("Delete level"),
N_("F5:"), N_("Update index from levels"),
0
};
#if 0
// fake gettext to register the following strings for I18N
_("F10")
_("Move clipboard levels")
#endif
LevelPackComposer::LevelPackComposer(bool enableEdit) :
isEditable (enableEdit), isModified (false) {
if (clipboard == NULL) {
std::vector<std::string> dummy;
clipboard = new lev::PersistentIndex(" ", false); // mark as incomplete
}
curIndex = dynamic_cast<lev::PersistentIndex *>(lev::Index::getCurrentIndex());
const video::VMInfo &vminfo = *video::GetInfo();
// Add navigation buttons
pgup = new ImageButton("ic-up", "ic-up1", this);
pgdown = new ImageButton("ic-down", "ic-down1", this);
start = new ImageButton("ic-top", "ic-top1", this);
end = new ImageButton("ic-bottom", "ic-bottom1", this);
Rect r(vminfo.width-30, 60, 20, 50);
r.y = 60;
add (pgup, r);
r.y += 60;
add (pgdown, r);
r.y = 60 + 240;
add (start, r);
r.y += 60;
add (end, r);
// Prepare level selection widget
levelwidget = new LevelWidget(false, isEditable);
levelwidget->set_listener(this);
ecl::Rect previewarea(10, 60, vminfo.width-50, vminfo.height-130);
levelwidget->realize (previewarea);
levelwidget->set_area (previewarea);
this->add(levelwidget);
// Information area
lbl_lpinfo = new Label();
lbl_clipinfo = new Label();
lbl_levelname = new Label();
lbl_clipcontent = new Label();
HList *hl = new HList;
hl->set_spacing(10);
hl->set_alignment(HALIGN_CENTER, VALIGN_TOP);
hl->set_default_size(vminfo.width/2 - 10, 28);
hl->add_back (lbl_lpinfo);
hl->add_back (lbl_clipinfo);
this->add (hl, Rect (5, 10, vminfo.width - 10, 28));
hl = new HList;
hl->set_spacing(10);
hl->set_alignment(HALIGN_CENTER, VALIGN_TOP);
clipContentWidth = vminfo.width/2 - 10;
hl->set_default_size(clipContentWidth, 28);
hl->add_back (lbl_levelname);
hl->add_back (lbl_clipcontent);
this->add (hl, Rect (5, 10+20, vminfo.width - 10, 28));
// Create buttons - positioning identical to Levelmenu
but_ignore = new StaticTextButton(N_("Undo"), this);
but_back = new StaticTextButton(N_("Ok"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(new Label());
commandHList->add_back(new Label());
commandHList->add_back(but_ignore);
commandHList->add_back(but_back);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
}
void LevelPackComposer::tick(double dtime)
{
levelwidget->tick(dtime);
static double timeaccu = 0.0;
// // info texts disappear after some time
// if (shown_text_ttl>0.0) {
// shown_text_ttl -= dtime;
// if (shown_text_ttl <= 0.0)
// shown_text = "";
// }
timeaccu += dtime;
if (timeaccu > 0.1) {
update_info();
timeaccu = 0.0;
}
}
bool LevelPackComposer::on_event (const SDL_Event &e) {
// Pass all events to the level widget first
bool handled=levelwidget->on_event(e);
if (!handled) {
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_DELETE:
if (SDL_GetModState() & KMOD_SHIFT) {
// delete clipboard
clipboard->clear();
handled=true;
} else {
// delete level
if (isEditable) {
int pos = curIndex->getCurrentPosition();
lev::Proxy * curProxy = curIndex->getCurrent();
if (curProxy == NULL) {
// levelpack is empty
handled=true;
break;
}
if (curIndex->isSource(curProxy) &&
backups.find(curProxy->getNormLevelPath()) == backups.end()) {
// mark as deletion candidate - the final check
// if we delete it really occurs on save
deletions.insert(curProxy->getNormLevelPath());
}
curIndex->erase(pos);
if (pos >= curIndex->size() && pos > 0)
curIndex->setCurrentPosition(pos-1);
levelwidget->syncFromIndexMgr();
isModified = true;
invalidate_all();
handled=true;
}
}
break;
case SDLK_F8:
if (isEditable) {
int pos = (curIndex->size() == 0) ? 0 : curIndex->getCurrentPosition() + 1;
for (int i = 0; i < clipboard->size(); i++) {
lev::Variation var = clipboard->getVariation(i);
curIndex->insertProxy(pos++, clipboard->getProxy(i), true,
var.ctrl, var.unit, var.target, var.extensions);
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_F9:
if (isEditable && !curIndex->isCross()) {
int pos = (curIndex->size() == 0) ? 0 : curIndex->getCurrentPosition() + 1;
for (int i = 0; i < clipboard->size(); i++) {
lev::Variation var = clipboard->getVariation(i);
lev::Proxy *levelCopy = clipboard->getProxy(i)->copy(app.userPath + "/levels",
curIndex->getPackPath(), true);
if (levelCopy == NULL) {
// insert a crossreference
curIndex->insertProxy(pos++, clipboard->getProxy(i), true,
var.ctrl, var.unit, var.target, var.extensions);
} else {
// insert reference to our copy
curIndex->insertProxy(pos++, levelCopy, true,
var.ctrl, var.unit, var.target, var.extensions);
backups.insert(levelCopy->getNormLevelPath());
deletions.erase(levelCopy->getNormLevelPath());
}
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_LEFT:
if (isEditable && (SDL_GetModState() & KMOD_ALT)) {
int pos = curIndex->getCurrentPosition();
if (pos > 0) {
curIndex->exchange(pos, pos-1);
levelwidget->syncFromIndexMgr();
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_RIGHT:
if (isEditable && (SDL_GetModState() & KMOD_ALT)) {
int pos = curIndex->getCurrentPosition();
if (pos < curIndex->size() - 1) {
curIndex->exchange(pos, pos+1);
levelwidget->syncFromIndexMgr();
isModified = true;
}
invalidate_all();
handled=true;
}
break;
case SDLK_F5:
if (isEditable) {
curIndex->updateFromProxies();
isModified = true;
invalidate_all();
handled=true;
}
break;
case SDLK_F1:
displayHelp(helptext, 200);
invalidate_all();
handled=true;
break;
default:
break;
}
}
else
handled = Menu::on_event (e);
}
return handled;
}
void LevelPackComposer::on_action(Widget *w) {
if (w==levelwidget) {
lev::Index *ind = lev::Index::getCurrentIndex();
int ilevel = ind->getCurrentPosition();
if (w->lastModifierKeys() & KMOD_SHIFT) {
lev::Variation var;
lev::Proxy * curProxy = lev::Index::getCurrentProxy();
if (curProxy->getNormPathType() != lev::Proxy::pt_absolute) {
// all but absolute commandline proxies may be put on the clipboard
if (curIndex != NULL)
var = curIndex->getVariation(curIndex->getCurrentPosition());
clipboard->appendProxy(curProxy, var.ctrl,
var.unit, var.target, var.extensions);
sound::EmitSoundEvent ("menuok");
} else {
sound::EmitSoundEvent ("menustop");
}
}
} else if (w == but_back) {
if (isModified) {
// save index
curIndex->save(true);
// delete levelfiles
std::set<std::string>::iterator it;
for (it = deletions.begin(); it != deletions.end(); it++) {
if (!curIndex->hasNormLevelPath(*it)) {
// delete plain files on user path - ignore system and zip levels
std::string path = app.userPath + "/levels/" + (*it);
std::remove((path + ".xml").c_str());
std::remove((path + ".lua").c_str());
}
}
// delete backups
std::string base = app.userPath + "/levels/";
for (it = backups.begin(); it != backups.end(); it++) {
std::remove((base + *it + ".xml~").c_str());
std::remove((base + *it + ".lua~").c_str());
}
}
Menu::quit();
} else if (w == but_ignore) {
if (isModified) {
// we need to reload the index
curIndex->loadDoc();
// restore backups
std::string base = app.userPath + "/levels/";
std::set<std::string>::iterator it;
for (it = backups.begin(); it != backups.end(); it++) {
std::remove((base + *it + ".xml").c_str());
std::rename((base + *it + ".xml~").c_str(), (base + *it + ".xml").c_str());
std::remove((base + *it + ".lua").c_str());
std::rename((base + *it + ".lua~").c_str(), (base + *it + ".lua").c_str());
}
}
Menu::quit();
} else if (w == pgup) {
levelwidget->page_up();
} else if (w == pgdown) {
levelwidget->page_down();
} else if (w == start) {
levelwidget->start();
} else if (w == end) {
levelwidget->end();
}
}
void LevelPackComposer::update_info() {
// Note: all format strings have to be translated directly
// as the formatted strings can no longer be translated.
// The instant language change is guaranteed by the frequent
// call of is method!
lev::Index *ind = lev::Index::getCurrentIndex();
int size = ind->size();
lev::Proxy *curProxy = ind->getCurrent();
lbl_lpinfo->set_text(ecl::strf(_("%s: %d levels"),
ind->getName().c_str(), size));
if (size == 0) {
// empty level pack
lbl_levelname->set_text ("-");
}
else {
lbl_levelname->set_text(ecl::strf("#%d:(%s)",
ind->getCurrentLevel(),
curProxy->getNormLevelPath().c_str()));
}
int csize = clipboard->size();
lbl_clipinfo->set_text(ecl::strf(_("Clipboard: %d levels"), csize));
if (csize == 0) {
// empty level pack
lbl_clipcontent->set_text ("-");
}
else {
std::string clipstring = clipboard->getProxy(0)->getTitle();
for (int i = 1; i < csize; i++)
clipstring += ", " + clipboard->getProxy(i)->getTitle();
lbl_clipcontent->set_text(clipstring);
if (enigma::GetFont("menufont")->get_width(clipstring.c_str()) > clipContentWidth)
lbl_clipcontent->set_alignment(HALIGN_RIGHT);
else
lbl_clipcontent->set_alignment(HALIGN_CENTER);
}
}
void LevelPackComposer::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Composer"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
if (isModified)
blit(gc, 0,0, enigma::GetImage("changed"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,61 @@
/*
* 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 GUI_LEVELPACKCOMPOSER_HH_INCLUDED
#define GUI_LEVELPACKCOMPOSER_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/LevelWidget.hh"
#include "lev/Proxy.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
#include <set>
namespace enigma { namespace gui {
class LevelPackComposer : public gui::Menu {
public:
LevelPackComposer(bool enableEdit);
void tick(double dtime);
bool on_event (const SDL_Event &e);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
void update_info();
static lev::PersistentIndex * clipboard;
bool isEditable;
bool isModified;
lev::PersistentIndex * curIndex;
std::set<std::string> deletions; // normLevelPath
std::set<std::string> backups; // normLevelPath
Widget *pgup, *pgdown, *start, *end;
LevelWidget *levelwidget;
Label *lbl_lpinfo; // Levelpack information
Label *lbl_levelname;
Label *lbl_clipinfo;
Label *lbl_clipcontent;
int clipContentWidth;
Widget *but_ignore;
Widget *but_back;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,540 @@
/*
* 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 "gui/LevelPackConfig.hh"
#include "gui/LevelPackComposer.hh"
#include "ecl.hh"
#include "errors.hh"
#include "nls.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
GroupButton::GroupButton(std::vector<std::string> groups, int pos) :
ValueButton(0, groups.size() - 1),
position (pos), groupNames (groups) {
init();
}
int GroupButton::get_value() const {
return position;
}
void GroupButton::set_value(int value) {
position = value;
}
std::string GroupButton::get_text(int value) const {
return groupNames[value];
}
/* ------------------- LevelmodeButton -------------------- */
LevelmodeButton::LevelmodeButton(bool initialMode) :
ImageButton("ic-link_copy","ic-link_copy",this), mode (initialMode) {
update();
}
bool LevelmodeButton::isLinkOnly() {
return mode;
}
void LevelmodeButton::update() {
if (mode)
ImageButton::set_images("ic-link","ic-link_copy");
else
ImageButton::set_images("ic-link_copy","ic-link");
}
void LevelmodeButton::on_action(Widget *)
{
mode = !mode;
update();
invalidate();
}
void LevelmodeButton::draw(ecl::GC &gc, const ecl::Rect &r) {
update();
ImageButton::draw(gc, r);
}
LevelPackConfig::LevelPackConfig(std::string indexName, std::string groupName,
bool forceGroupReasign) : isReasignOnly (forceGroupReasign),
undo_quit (false), didEditMetaData (false), titleTF (NULL) {
const video::VMInfo &vminfo = *video::GetInfo();
if (indexName.empty())
// new levelpack
packIndex = new lev::PersistentIndex(" ", false,
INDEX_DEFAULT_PACK_LOCATION, "",
INDEX_STD_FILENAME, lev::Index::getCurrentGroup()); // mark as incomplete
else
packIndex = lev::Index::findIndex(indexName);
ASSERT (packIndex != NULL, XFrontend, "not existing index Name");
persIndex = dynamic_cast<lev::PersistentIndex *>(packIndex);
isPersistent = (persIndex != NULL);
isEditable = isPersistent ? persIndex->isUserEditable() : false;
// build a list of allowed group
std::vector<std::string> groups = lev::Index::getGroupNames();
// eliminate pseudo group "All Packs"
std::vector<std::string>::iterator itg = groups.begin();
while (itg != groups.end()) {
if (*itg == INDEX_ALL_PACKS) {
itg = groups.erase(itg);
break;
}
if (itg != groups.end())
itg++;
}
// add pseudo group "[Every Group]"
groups.push_back(std::string("[") + INDEX_EVERY_GROUP +"]");
intialGroupPosition = groups.size() - 1; // INDEX_EVERY_GROUP as default
// mark index's default group with square brackets and find current group
bool defaultGroupFound = false;
for (int i = 0; i < groups.size(); i++) {
if (groups[i] == packIndex->getGroupName()) {
intialGroupPosition = i;
}
if (groups[i] == packIndex->getDefaultGroupName()) {
groups[i] = std::string("[") + groups[i] +"]";
defaultGroupFound = true;
}
}
if (!defaultGroupFound) {
groups.push_back(std::string("[") + packIndex->getDefaultGroupName() +"]");
}
groupButton = new GroupButton(groups, intialGroupPosition);
// index location list setup
std::vector<lev::Index *> * allIndices = lev::Index::getGroup(INDEX_ALL_PACKS);
for (int i = 0; i < allIndices->size(); i++)
locationList.push_back((*allIndices)[i]->getName());
position = -1;
for (int i = 0; i < locationList.size(); i++) {
if (locationList[i] == indexName) {
position = i;
break;
}
}
oldPosition = position;
if (position < 0) {
// append new levelpack as last
locationList.push_back(indexName);
position = locationList.size() - 1;
}
VList * titleLeftVList = new VList;
titleLeftVList->set_spacing(11);
titleLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
titleLeftVList->set_default_size(140, 35);
Label * titleLabel = new Label(N_("Levelpack:"), HALIGN_RIGHT);
Label * ownerLabel = new Label(N_("Owner:"), HALIGN_RIGHT);
Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT);
Label * loactionLabel1 = new Label(N_("Location"), HALIGN_LEFT);
Label * loactionLabel2 = new Label(N_("in [All Packs]:"), HALIGN_RIGHT);
titleLeftVList->add_back(titleLabel);
if (!isReasignOnly) {
titleLeftVList->add_back(ownerLabel);
}
titleLeftVList->add_back(groupLabel);
if (!isReasignOnly) {
titleLeftVList->add_back(new Label());
titleLeftVList->add_back(loactionLabel1);
titleLeftVList->add_back(loactionLabel2);
titleLeftVList->add_back(new Label());
titleLeftVList->add_back(new Label());
}
valueLeftVList = new VList;
valueLeftVList->set_spacing(11);
valueLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
valueLeftVList->set_default_size(160, 35);
titleValueLabel = new UntranslatedLabel(indexName, HALIGN_CENTER);
ownerValueLabel = new UntranslatedLabel(isPersistent ? persIndex->getOwner() : "System");
pre2Index = new UntranslatedLabel();
pre1Index = new UntranslatedLabel();
thisIndex = new UntranslatedLabel();
post1Index = new UntranslatedLabel();
post2Index = new UntranslatedLabel();
valueLeftVList->add_back(titleValueLabel);
if (!isReasignOnly) {
valueLeftVList->add_back(ownerValueLabel);
}
valueLeftVList->add_back(groupButton);
if (!isReasignOnly) {
valueLeftVList->add_back(pre2Index);
valueLeftVList->add_back(pre1Index);
valueLeftVList->add_back(thisIndex);
valueLeftVList->add_back(post1Index);
valueLeftVList->add_back(post2Index);
}
VList * scrollVList = new VList;
scrollVList->set_spacing(12);
scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
scrollVList->set_default_size(30, 35);
scrollUp = new ImageButton("ic-up", "ic-up1", this);
scrollDown = new ImageButton("ic-down", "ic-down1", this);
scrollVList->add_back(scrollUp);
scrollVList->add_back(scrollDown);
VList * metaVList = new VList;
metaVList->set_spacing(12);
metaVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
metaVList->set_default_size(140, 35);
if (isEditable)
but_metadata = new StaticTextButton(N_("Edit Metadata"), this);
else
but_metadata = new Label();
Label * releaseLabel = new Label(N_("Release:"), HALIGN_RIGHT);
Label * revisionLabel = new Label(N_("Revision:"), HALIGN_RIGHT);
Label * compatibilityLabel = new Label(N_("Compatibility:"), HALIGN_RIGHT);
Label * defLocationLabel = new Label(N_("Default Location:"), HALIGN_RIGHT);
Label * crossmodeLabel = new Label(N_("Level types:"), HALIGN_RIGHT);
if (!isReasignOnly) {
metaVList->add_back(but_metadata);
metaVList->add_back(new Label());
if (WizardMode) {
metaVList->add_back(releaseLabel);
metaVList->add_back(revisionLabel);
} else {
metaVList->add_back(new Label());
metaVList->add_back(new Label());
}
metaVList->add_back(crossmodeLabel);
if (WizardMode) {
metaVList->add_back(compatibilityLabel);
} else {
metaVList->add_back(new Label());
}
metaVList->add_back(defLocationLabel);
metaVList->add_back(new Label());
}
valueMetaVList = new VList;
valueMetaVList->set_spacing(12);
valueMetaVList->set_alignment(HALIGN_CENTER, VALIGN_CENTER);
valueMetaVList->set_default_size(75, 35);
Widget * levelmodeWidget;
if (indexName.empty()){
levelmode = new LevelmodeButton(false);
levelmodeWidget = levelmode;
} else {
levelmodeWidget = new Image(isPersistent && !(persIndex->isCross()) ?
"ic-link_copy" : "ic-link");
}
defLocationValueLabel = new Label(ecl::strf("%g", packIndex->getDefaultLocation()));
releaseValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRelease()) : "-");
revisionValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRevision()) : "-");
compatibilityValueLabel = new Label(isPersistent ? ecl::strf("%.2f", persIndex->getCompatibility()) : "-");
if (!isReasignOnly) {
valueMetaVList->add_back(new Label());
valueMetaVList->add_back(new Label());
if (WizardMode) {
valueMetaVList->add_back(releaseValueLabel);
valueMetaVList->add_back(revisionValueLabel);
} else {
valueMetaVList->add_back(new Label());
valueMetaVList->add_back(new Label());
}
valueMetaVList->add_back(levelmodeWidget);
if (WizardMode) {
valueMetaVList->add_back(compatibilityValueLabel);
} else {
valueMetaVList->add_back(new Label());
}
valueMetaVList->add_back(defLocationValueLabel);
valueMetaVList->add_back(new Label());
}
if (isReasignOnly) {
this->add(titleLeftVList, Rect(vminfo.width/2 - 270, 15, 140, vminfo.height-97));
this->add(valueLeftVList, Rect(vminfo.width/2 - 80, 15, 160, vminfo.height-97));
} else {
this->add(titleLeftVList, Rect(vminfo.width/2 - 300, 15, 140, vminfo.height-97));
this->add(valueLeftVList, Rect(vminfo.width/2 - 140, 15, 160, vminfo.height-97));
this->add(scrollVList, Rect(vminfo.width/2 + 30, 15+3*(35+12) + (vminfo.height-480)/2, 30, 5*35+4*12));
this->add(metaVList, Rect(vminfo.width/2 + 80, 15, 140, vminfo.height-97));
this->add(valueMetaVList, Rect(vminfo.width/2 + 235, 15, 75, vminfo.height-97));
}
errorLabel = new Label("", HALIGN_CENTER);
this->add(errorLabel, Rect(10, vminfo.height-97, vminfo.width-20, 35));
if (isReasignOnly)
errorLabel->set_text(N_("Please reasign levelpack to another group for group deletion"));
// Create buttons - positioning identical to Levelmenu
but_edit = new StaticTextButton(N_("Compose Pack"), this);
if (isPersistent && persIndex->isUpdatable() && persIndex->isCross()) {
but_update = new StaticTextButton(N_("Update Pack"), this);
} else {
but_update = new Label();
}
but_ignore = new StaticTextButton(N_("Undo"), this);
but_back = new StaticTextButton(N_("Ok"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
if (isReasignOnly) {
commandHList->add_back(new Label());
commandHList->add_back(new Label());
} else {
commandHList->add_back(but_edit);
commandHList->add_back(but_update);
}
commandHList->add_back(but_ignore);
commandHList->add_back(but_back);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
updateLocationList();
if (indexName.empty())
// new levelpack
switchToMetadataEdit();
}
void LevelPackConfig::updateLocationList() {
pre2Index->set_text((position > 1) ? locationList[position - 2] : "");
pre1Index->set_text((position > 0) ? locationList[position - 1] : "");
thisIndex->set_text(didEditMetaData ? titleTF->getText() : packIndex->getName());
post1Index->set_text((position < locationList.size() - 1) ? locationList[position + 1] : "");
post2Index->set_text((position < locationList.size() - 2) ? locationList[position + 2] : "");
}
void LevelPackConfig::switchToMetadataEdit() {
if (!didEditMetaData) {
didEditMetaData = true;
titleTF = new TextField(titleValueLabel->getText(), this);
valueLeftVList->exchange_child(titleValueLabel, titleTF);
delete titleValueLabel;
ownerTF = new TextField(ownerValueLabel->getText());
valueLeftVList->exchange_child(ownerValueLabel, ownerTF);
delete ownerValueLabel;
defLocationTF = new TextField(defLocationValueLabel->getText());
valueMetaVList->exchange_child(defLocationValueLabel, defLocationTF);
delete defLocationValueLabel;
if (WizardMode) {
releaseTF = new TextField(releaseValueLabel->getText());
valueMetaVList->exchange_child(releaseValueLabel, releaseTF);
delete releaseValueLabel;
revisionTF = new TextField(revisionValueLabel->getText());
valueMetaVList->exchange_child(revisionValueLabel, revisionTF);
delete revisionValueLabel;
compatibilityTF = new TextField(compatibilityValueLabel->getText());
valueMetaVList->exchange_child(compatibilityValueLabel, compatibilityTF);
delete compatibilityValueLabel;
}
}
}
bool LevelPackConfig::isUndoQuit() {
return undo_quit;
}
bool LevelPackConfig::doChanges() {
// change metadata
if (didEditMetaData) {
// the Index is persistent, user editabel and the user did switch to edit mode
bool needSave = false;
bool isNewIndex = persIndex->getName().empty();
// check for valid input
// title
std::string newtitle = titleTF->getText();
std::string::size_type lastChar = newtitle.find_last_not_of(" ");
if (lastChar == std::string::npos) {
// the title is effectively an empty string
errorLabel->set_text(N_("Error: empty title not allowed - press \"Undo\" to exit without modifications"));
return false;
}
// strip off trailing and leading spaces
newtitle = newtitle.substr(0 , lastChar + 1);
newtitle = newtitle.substr(newtitle.find_first_not_of(" "));
if (newtitle != persIndex->getName()) {
if (isNewIndex) {
// check for filename usability of title
const std::string validChars("_- .#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
if (newtitle.find_first_not_of(validChars, 0) != std::string::npos ||
(newtitle.length() >= 1 && newtitle[0] == '.')) {
errorLabel->set_text(N_("Error: use only \"a-zA-Z0-9 _-#\" for levelpack title"));
return false;
}
// set packPath to cross if link only
if (levelmode->isLinkOnly())
persIndex->markNewAsCross();
}
if (!persIndex->setName(newtitle)) {
errorLabel->set_text(N_("Error: title already in use - choose another title"));
return false;
}
needSave = true;
}
// check rest for need of save
if (ownerTF->getText() != persIndex->getOwner()) {
persIndex->setOwner(ownerTF->getText());
needSave = true;
}
if (defLocationTF->getText() != ecl::strf("%g", packIndex->getDefaultLocation())) {
double d = 0;
// check value - keep old value on error
if ((sscanf(defLocationTF->getText().c_str(),"%lg", &d) == 1) &&
d > 0) {
packIndex->setDefaultLocation(d);
needSave = true;
}
}
if (WizardMode) {
if (releaseTF->getText() != ecl::strf("%d", persIndex->getRelease())) {
int i = 0;
// check value - keep old value on error
if ((sscanf(releaseTF->getText().c_str(),"%d", &i) == 1) &&
i > 0) {
persIndex->setRelease(i);
needSave = true;
}
}
if (revisionTF->getText() != ecl::strf("%d", persIndex->getRevision())) {
int i = 0;
// check value - keep old value on error
if ((sscanf(revisionTF->getText().c_str(),"%d", &i) == 1) &&
i > 0) {
persIndex->setRevision(i);
needSave = true;
}
}
if (compatibilityTF->getText() != ecl::strf("%.2f", persIndex->getCompatibility())) {
double d = 0;
// check value - keep old value on error
if ((sscanf(compatibilityTF->getText().c_str(),"%lg", &d) == 1) &&
d >= 1) {
persIndex->setCompatibility(d);
needSave = true;
}
}
}
// save
if (needSave)
persIndex->save();
if (isNewIndex) {
lev::Index::registerIndex(persIndex);
lev::Index::setCurrentIndex(persIndex->getName());
}
}
// regroup
if (groupButton->get_value() != intialGroupPosition) {
std::string newGroupName = groupButton->get_text(groupButton->get_value());
// strip off square brackets used to mark default and pseudo groups
if (newGroupName.size() > 2 && newGroupName[0] == '[' &&
newGroupName[newGroupName.size() -1] == ']') {
newGroupName = newGroupName.substr(1, newGroupName.size() - 2);
}
packIndex->moveToGroup(newGroupName);
} else if (isReasignOnly) {
// the user did not reasign - take as an undo request
undo_quit = true;
}
// relocate
if (position != oldPosition)
packIndex->locateBehind(position > 0 ? locationList[position - 1] : "");
return true;
}
void LevelPackConfig::on_action(Widget *w) {
if (w == but_back) {
if (doChanges())
Menu::quit();
else
invalidate_all();
} else if (w == but_ignore) {
if (packIndex->getName().empty()) {
delete packIndex;
}
undo_quit = true;
Menu::quit();
} else if (w == but_update) {
if (isPersistent && doChanges()) {
persIndex->load(false, true);
persIndex->save(true);
Menu::quit();
}
invalidate_all();
} else if (w == but_edit) {
if (doChanges()) {
LevelPackComposer m(isEditable);
m.manage();
Menu::quit();
} else {
invalidate_all();
}
} else if (w == scrollUp) {
if (position > 0) {
std::string tmp = locationList[position];
locationList[position] = locationList[position - 1];
locationList[position - 1] = tmp;
position--;
updateLocationList();
invalidate_all();
}
} else if (w == scrollDown) {
if (position < locationList.size() - 1) {
std::string tmp = locationList[position];
locationList[position] = locationList[position + 1];
locationList[position + 1] = tmp;
position++;
updateLocationList();
invalidate_all();
}
} else if (w == but_metadata && !didEditMetaData) {
switchToMetadataEdit();
invalidate_all();
} else if (w == titleTF && w != NULL) {
thisIndex->set_text(titleTF->getText());
}
}
void LevelPackConfig::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Configuration"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,112 @@
/*
* 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 GUI_LEVELPACKCONFIG_HH_INCLUDED
#define GUI_LEVELPACKCONFIG_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
namespace enigma { namespace gui {
class GroupButton : public ValueButton {
public:
GroupButton(std::vector<std::string> groups, int pos);
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
private:
std::vector<std::string> groupNames;
int position;
};
class LevelmodeButton : public ImageButton {
// ActionListener interface.
void on_action(Widget *);
public:
LevelmodeButton(bool initialMode = false);
bool isLinkOnly();
virtual void draw(ecl::GC &gc, const ecl::Rect &r);
private:
void update();
bool mode;
};
class LevelPackConfig : public gui::Menu {
public:
LevelPackConfig (std::string indexName, std::string groupName = "",
bool forceGroupReasign = false);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
bool isUndoQuit();
private:
void updateLocationList();
void switchToMetadataEdit();
bool doChanges();
lev::Index *packIndex;
lev::PersistentIndex *persIndex;
bool isPersistent;
bool isEditable;
TextField *tf_packName;
GroupButton *groupButton;
int intialGroupPosition;
VList *valueLeftVList;
Label *titleValueLabel;
TextField *titleTF;
Label *ownerValueLabel;
TextField *ownerTF;
VList *valueMetaVList;
LevelmodeButton *levelmode;
Label *defLocationValueLabel;
TextField *defLocationTF;
Label *releaseValueLabel;
TextField *releaseTF;
Label *revisionValueLabel;
TextField *revisionTF;
Label *compatibilityValueLabel;
TextField *compatibilityTF;
Label *pre2Index;
Label *pre1Index;
Label *thisIndex;
Label *post1Index;
Label *post2Index;
Widget *scrollUp;
Widget *scrollDown;
Label *errorLabel;
Widget *but_metadata;
Widget *but_up;
Widget *but_down;
Widget *but_edit;
Widget *but_update;
Widget *but_ignore;
Widget *but_back;
bool isReasignOnly;
bool didEditMetaData;
bool undo_quit;
std::vector<std::string> locationList;
int position; // new position of index in locationList that the user selected
int oldPosition; // position of index when entering menu, -1 for new index
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,506 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, 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 "gui/LevelPackMenu.hh"
#include "gui/LevelMenu.hh"
#include "gui/LPGroupConfig.hh"
#include "gui/LevelPackConfig.hh"
#include "gui/SearchMenu.hh"
#include "gui/HelpMenu.hh"
#include "ecl.hh"
#include "errors.hh"
#include "nls.hh"
#include "video.hh"
#include "lev/Index.hh"
#include "lev/PersistentIndex.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- Level Pack Menu -------------------- */
std::map<std::string, std::string> LevelPackMenu::groupLastSelectedIndex;
std::map<std::string, int> LevelPackMenu::groupLastSelectedColumn;
std::string LevelPackMenu::lastGroupName;
int LevelPackMenu::firstDisplayedGroup = 0;
static const char *helptext[] = {
N_("Left column:"), N_("Levelpack groups"),
N_("Right columns:"), N_("Levelpacks of selected group"),
N_("Left click:"), N_("Select group or levelpack"),
N_("Right or control click:"), N_("Configure group or levelpack"),
0
};
LevelPackMenu::LevelPackMenu() : packsHList (NULL), groupsVList (NULL),
scrollLeft (NULL), scrollRight (NULL), scrollUp (NULL),
scrollDown (NULL), isLevelMenuSubmenu (false) {
const video::VMInfo &vminfo = *video::GetInfo();
vm = vminfo.videomode;
// Create buttons - positioning identical to Levelmenu
but_new = new StaticTextButton(N_("New Group"), this);
but_search = new StaticTextButton(N_("Search"), this);
but_level = new StaticTextButton(N_("Start Game"), this);
but_main = new StaticTextButton(N_("Main Menu"), this);
commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(but_new);
commandHList->add_back(but_search);
commandHList->add_back(but_level);
commandHList->add_back(but_main);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
}
void LevelPackMenu::setupMenu() {
static struct SpacingConfig {
int packcolumns, rows;
int vmargin, vrow_row;
int hmargin, hgroup_pack, hscrollbutton, hscroll_pack, hpack_pack;
} param[video::VM_COUNT] = {
{ // VM_640x480
2, 9,
15, 10,
20, 36, 22, 10, 20
},
{ // VM_640x512
2, 9,
15, 10,
20, 36, 22, 10, 20
},
{ // VM_800x600
3, 11,
15, 13,
15, 36, 22, 10, 15
},
{ // VM_1024x768
4, 15,
15, 10,
30, 36, 22, 12, 20
}
};
if (groupsVList != NULL) {
groupsVList->clear();
remove_child(groupsVList);
delete groupsVList;
groupsVList = NULL;
scrollUp = NULL; // deleted with groupsVList
scrollDown = NULL; // deleted with groupsVList
}
if (packsHList != NULL) {
packsHList->clear();
remove_child(packsHList);
delete packsHList;
packsHList = NULL;
}
if (scrollLeft != NULL) {
remove_child(scrollLeft);
delete scrollLeft;
scrollLeft = NULL;
}
if (scrollRight != NULL) {
remove_child(scrollRight);
delete scrollRight;
scrollRight = NULL;
}
packButtons.clear();
groupButtons.clear();
std::vector<std::string> groupNames = lev::Index::getGroupNames();
int groupCount = groupNames.size();
std::string curGroupName = lev::Index::getCurrentGroup();
bool needUpScroll = false;
bool needDownScroll = false;
int numDisplayGroups = param[vm].rows;
int usedGroupRows = (groupCount > numDisplayGroups) ? numDisplayGroups : groupCount;
// correct scroll attempts and screen resolution changes
firstDisplayedGroup = ecl::Clamp<int>(firstDisplayedGroup, 0,
(groupCount > numDisplayGroups) ? groupCount - numDisplayGroups : 0);
needUpScroll = firstDisplayedGroup > 0;
needDownScroll = firstDisplayedGroup < groupCount - numDisplayGroups;
if (curGroupName != lastGroupName) {
// group changed by indirect user action - ensure it is visible
int curGroupPos = getGroupPosition(&groupNames, curGroupName);
if (curGroupPos != -1) {
if (curGroupPos <= firstDisplayedGroup ) {
if (curGroupPos <= 1) {
needUpScroll = false;
firstDisplayedGroup = 0;
} else {
needUpScroll = true;
firstDisplayedGroup = curGroupPos -1;
}
needDownScroll = firstDisplayedGroup < groupCount - numDisplayGroups;
} else if (curGroupPos >= firstDisplayedGroup + numDisplayGroups - 1) {
if (curGroupPos >= groupCount - 2) {
needDownScroll = false;
firstDisplayedGroup = groupCount - numDisplayGroups;
} else {
needDownScroll = true;
firstDisplayedGroup = curGroupPos - numDisplayGroups + 2;
}
if (firstDisplayedGroup < 0)
firstDisplayedGroup = 0;
needUpScroll = firstDisplayedGroup > 0;
}
}
}
groupsVList = new VList;
groupsVList->set_spacing(param[vm].vrow_row);
groupsVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
groupsVList->set_default_size(160, 35);
for (int i = 0; i < usedGroupRows; i++) {
if (i == 0 && needUpScroll) {
scrollUp = new ImageButton("ic-up", "ic-up1", this);
groupsVList->add_back(scrollUp);
} else if (i == usedGroupRows -1 && needDownScroll) {
scrollDown = new ImageButton("ic-down", "ic-down1", this);
groupsVList->add_back(scrollDown);
} else {
TextButton * button = new UntranslatedStaticTextButton(
groupNames[firstDisplayedGroup + i], this);;
groupButtons.push_back(button);
groupsVList->add_back(button);
}
}
this->add(groupsVList, Rect(param[vm].hmargin, param[vm].vmargin,
160, param[vm].rows * 35 +
(param[vm].rows - 1) * param[vm].vrow_row));
lastGroupName = curGroupName;
std::vector<lev::Index *> * group = lev::Index::getGroup(curGroupName);
ASSERT(group != NULL, XFrontend,"");
unsigned packCount = group->size();
int posCurrentIndex = getIndexPosition(group, lev::Index::getCurrentIndex()->getName());
int selectedColumn = lev::Index::getGroupSelectedColumn(curGroupName);
int colCurrentIndex = 0;
int nextPack = 0; // pack displayed at top of first display column
if (selectedColumn != INDEX_GROUP_COLUMN_UNKNOWN ||
groupLastSelectedIndex.find(curGroupName) == groupLastSelectedIndex.end()) {
colCurrentIndex = checkColumn(param[vm].rows, param[vm].packcolumns,
packCount, posCurrentIndex, selectedColumn);
nextPack = (posCurrentIndex / param[vm].rows - colCurrentIndex) * param[vm].rows;
} else {
// the user selected a new level pack and the column was not yet
// calculated: we try to keep the display unchanged in respect of
// of the last selected pack and if necessary scroll minimum amount
// of columns
int posLastIndex = getIndexPosition(group,groupLastSelectedIndex[curGroupName]);
int colLastIndex = checkColumn(param[vm].rows, param[vm].packcolumns,
packCount, posLastIndex, groupLastSelectedColumn[curGroupName]);
nextPack = (posLastIndex / param[vm].rows - colLastIndex) * param[vm].rows;
if (posCurrentIndex < nextPack) {
// current index would be left of display - we need to scroll left
nextPack -= (((nextPack - posCurrentIndex - 1)/param[vm].rows) + 1) *
param[vm].rows;
colCurrentIndex = 0;
} else if (posCurrentIndex < nextPack + param[vm].rows * param[vm].packcolumns) {
// current index is still visible - keep nextPack
colCurrentIndex = (posCurrentIndex - nextPack) / param[vm].rows;
} else {
// current index would be right of display - we need to scroll right
nextPack += (((posCurrentIndex - nextPack)/param[vm].rows) -
(param[vm].packcolumns - 1)) * param[vm].rows;
colCurrentIndex = param[vm].packcolumns - 1;
}
}
bool needRightScroll = packCount > nextPack + param[vm].rows * param[vm].packcolumns;
bool needLeftScroll = nextPack > 0;
lev::Index::setGroupSelectedColumn(curGroupName, colCurrentIndex);
groupLastSelectedIndex[curGroupName] = lev::Index::getCurrentIndex()->getName();
groupLastSelectedColumn[curGroupName] = colCurrentIndex;
packsHList = new HList;
packsHList->set_spacing(param[vm].hpack_pack);
packsHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
packsHList->set_default_size(160, param[vm].rows*35 +
(param[vm].rows - 1) * param[vm].vrow_row);
for (int col = 0; col < param[vm].packcolumns; col++) {
if (packCount - nextPack > 0) {
VList * pl = new VList;
pl->set_spacing (param[vm].vrow_row);
// first column is centered - if it is full it is like top alignment:
pl->set_alignment (HALIGN_LEFT, col == 0 ? VALIGN_CENTER : VALIGN_TOP);
pl->set_default_size (160, 35);
for (int row = 0; row < param[vm].rows; row++) {
if (nextPack < packCount) {
lev::Index *ind = (*group)[nextPack];
TextButton * button = new UntranslatedStaticTextButton(ind->getName(), this);
packButtons.push_back(button);
pl->add_back(button);
nextPack++;
} else
break;
}
packsHList->add_back(pl);
} else
break;
}
this->add(packsHList, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack +
param[vm].hscrollbutton + param[vm].hscroll_pack,
param[vm].vmargin,
param[vm].packcolumns * 160 + (param[vm].packcolumns - 1) *
param[vm].hpack_pack,
param[vm].rows * 35 +
(param[vm].rows - 1) * param[vm].vrow_row));
if (needLeftScroll) {
scrollLeft = new ImageButton("ic-left", "ic-left1", this);
this->add(scrollLeft, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack,
param[vm].vmargin + param[vm].rows / 2 * (35 + param[vm].vrow_row),
param[vm].hscrollbutton, 35));
}
if (needRightScroll) {
scrollRight = new ImageButton("ic-right", "ic-right1", this);
this->add(scrollRight, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack +
param[vm].hscrollbutton + 2 * param[vm].hscroll_pack +
param[vm].packcolumns * 160 + (param[vm].packcolumns - 1) *
param[vm].hpack_pack,
param[vm].vmargin + param[vm].rows / 2 * (35 + param[vm].vrow_row),
param[vm].hscrollbutton, 35));
}
}
void LevelPackMenu::manageLevelMenu() {
bool finished = false;
while (!finished) {
{
LevelMenu m;
if (!m.manage() || m.isMainQuit()) {
// ESC or Main button has been pressed in LevelMenu -
// the user wants to return
finished = true;
}
}
if (!finished) {
// the user left LevelMenu via LevelPack button
this->isLevelMenuSubmenu = true;
if (this->manage()) {
// not ESC - the user pressed Main button
finished = true;
} else {
// the user pressed ESC - return to LevelMenu
}
}
}
}
bool LevelPackMenu::manage() {
setupMenu();
updateHighlight();
return Menu::manage();
}
bool LevelPackMenu::on_event (const SDL_Event &e) {
switch (e.type) {
case SDL_KEYDOWN:
SDLKey keysym = e.key.keysym.sym;
switch (keysym) {
case SDLK_F1:
displayHelp(helptext, 200);
invalidate_all();
return true;
default:
break;
}
break;
}
return false;
}
void LevelPackMenu::on_action(Widget *w) {
if (w == but_main) {
Menu::quit();
} else if (w == but_new) {
LPGroupConfig m("");
m.manage();
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == but_level) {
LevelMenu m;
if (!m.manage() && isLevelMenuSubmenu || m.isMainQuit()) {
// ESC in LevelMenu in case we are a submenu of LevelMenu or
// Main button has been pressed in LevelMenu
Menu::quit();
}
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == but_search) {
SearchMenu ml;
ml.manage();
if (ml.isSearchQuit()) {
// show search result levelpack
LevelMenu ml;
if (!ml.manage() && isLevelMenuSubmenu || ml.isMainQuit()) {
// ESC in LevelMenu in cade we are a submenu of LevelMenu or
// Main button has been pressed in LevelMenu
Menu::quit();
}
}
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollUp) {
firstDisplayedGroup--;
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollDown) {
firstDisplayedGroup++;
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w->get_parent() == groupsVList){
lev::Index::setCurrentGroup(dynamic_cast<TextButton *>(w)->get_text());
if ((w->lastMouseButton() == SDL_BUTTON_RIGHT ||
w->lastModifierKeys() & KMOD_CTRL) &&
dynamic_cast<TextButton *>(w)->get_text() != INDEX_ALL_PACKS) {
// configure group
// INDEX_ALL_PACKS cannot be renamed, deleted, no packs can be created
LPGroupConfig m(dynamic_cast<TextButton *>(w)->get_text());
m.manage();
lastGroupName = ""; // the group may have moved, force a recalc
}
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w->get_parent()->get_parent() == packsHList){
lev::Index::setCurrentIndex(dynamic_cast<TextButton *>(w)->get_text());
if (w->lastMouseButton() == SDL_BUTTON_RIGHT ||
w->lastModifierKeys() & KMOD_CTRL) {
// configure levelpack index
LevelPackConfig m(dynamic_cast<TextButton *>(w)->get_text());
m.manage();
} else {
LevelMenu m;
if (!m.manage() && isLevelMenuSubmenu || m.isMainQuit()) {
// ESC in LevelMenu in case we are a submenu of LevelMenu or
// Main button has been pressed in LevelMenu
Menu::quit();
return;
}
}
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollLeft) {
std::string curGroupName = lev::Index::getCurrentGroup();
lev::Index::setGroupSelectedColumn(curGroupName,
lev::Index::getGroupSelectedColumn(curGroupName) + 1);
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
} else if (w == scrollRight) {
std::string curGroupName = lev::Index::getCurrentGroup();
lev::Index::setGroupSelectedColumn(curGroupName,
lev::Index::getGroupSelectedColumn(curGroupName) - 1);
reset_active_widget(); // we will delete it with setup
setupMenu();
updateHighlight();
invalidate_all();
}
}
void LevelPackMenu::updateHighlight() {
for (int i = 0; i < packButtons.size(); i++) {
TextButton * button = packButtons[i];
if (button->get_text() == lev::Index::getCurrentIndex()->getName())
button->setHighlight(true);
else
button->setHighlight(false);
}
for (int i = 0; i < groupButtons.size(); i++) {
TextButton * button = groupButtons[i];
if (button->get_text() == lev::Index::getCurrentGroup())
button->setHighlight(true);
else
button->setHighlight(false);
}
}
void LevelPackMenu::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Level Pack Menu"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
int LevelPackMenu::getGroupPosition(std::vector<std::string> * groups, std::string groupName) {
std::vector<std::string>::iterator it;
int i = 0;
for (it = groups->begin(); it != groups->end(); it++, i++) {
if ((*it) == groupName) {
return i;
}
}
return -1;
}
int LevelPackMenu::getIndexPosition(std::vector<lev::Index *> * group, std::string indexName) {
std::vector<lev::Index *>::iterator it;
int i = 0;
for (it = group->begin(); it != group->end(); it++, i++) {
if ((*it)->getName() == indexName) {
return i;
}
}
return -1;
}
int LevelPackMenu::checkColumn(int rows, int columns, int size,
int position, int oldColumn) {
int naturalColumn = position / rows;
int numColumns = (size - 1) / rows + 1;
if (oldColumn == INDEX_GROUP_COLUMN_UNKNOWN)
return (naturalColumn > columns) ? columns - 1 : naturalColumn;
else
return ecl::Clamp<int>(oldColumn, naturalColumn -
((numColumns > columns) ? (numColumns - columns) : 0),
naturalColumn);
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, 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 GUI_LEVELPACKMENU_HH_INCLUDED
#define GUI_LEVELPACKMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "video.hh"
#include "lev/Index.hh"
#include <map>
#include <string>
namespace enigma { namespace gui {
/* -------------------- LevelPackMenu -------------------- */
/**
* The levelpack selection menu.
*
* The used strategy for horizontal scrolling of the levelpack columns
* is to avoid scrolling as long as the target levelpack is still visible.
* The current levelpack may be scrolled out of visibility by using the
* scoll buttons within this menu. But if the user changes the current
* levelpack via indirect methods besides
* using the levelpack menu, f.e. the jumpto command, we ensure that
* the new current levelpack is visible when the menu is redisplayed.
* If it is necessary to scroll we scroll only the minimum amount of columns
* needed to display the current levelpack. Even on screen resolution changes
* we try to keep the column of the current levelpack unchanged.
*/
class LevelPackMenu : public Menu {
public:
LevelPackMenu();
void manageLevelMenu();
virtual bool manage();
bool on_event (const SDL_Event &e);
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
private:
static std::map<std::string, std::string> groupLastSelectedIndex;
static std::map<std::string, int> groupLastSelectedColumn;
static std::string lastGroupName;
static int firstDisplayedGroup;
video::VideoModes vm;
std::vector<TextButton *> packButtons;
std::vector<TextButton *> groupButtons;
HList *packsHList;
ImageButton *scrollLeft;
ImageButton *scrollRight;
ImageButton *scrollUp;
ImageButton *scrollDown;
VList *groupsVList;
HList *commandHList;
Widget *but_search;
Widget *but_new;
Widget *but_level;
Widget *but_main;
bool isLevelMenuSubmenu;
void setupMenu();
void updateHighlight();
int getGroupPosition(std::vector<std::string> * groups, std::string groupName);
int getIndexPosition(std::vector<lev::Index *> * group, std::string indexName);
int checkColumn(int rows, int columns, int size, int position, int oldColumn);
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, 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 "gui/LevelPreviewCache.hh"
#include "ecl.hh"
#include "file.hh"
#include "game.hh"
#include "main.hh"
#include "video.hh"
#include "lev/Proxy.hh"
#include "SDL.h"
#include <fstream>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- LevelPreviewCache -------------------- */
LevelPreviewCache *LevelPreviewCache::theSingleton = 0;
LevelPreviewCache* LevelPreviewCache::instance() {
if (theSingleton == 0) {
theSingleton = new LevelPreviewCache();
}
return theSingleton;
}
LevelPreviewCache::LevelPreviewCache() : cachedIndex (NULL) {
}
void LevelPreviewCache::clear() {
Log << "LevelPreviewCache clear\n";
cache.clear();
imgCache.clear();
}
Surface *LevelPreviewCache::getPreview(lev::Proxy *levelProxy,
bool allowGeneration, bool &didGenerate) {
didGenerate = false;
std::string mapIndex = levelProxy->getId() +
ecl::strf("#%d", levelProxy->getReleaseVersion());
// first look in cache
PreviewMap::iterator i = cache.find(mapIndex);
if (i != cache.end())
return i->second;
// we have to cache a new preview - check if we should clear cache first
if (cachedIndex == NULL) {
// remember the index we are caching
cachedIndex = lev::Index::getCurrentIndex();
} else if (cachedIndex != lev::Index::getCurrentIndex()) {
//the index changed - cache only previews from new index
clear();
cachedIndex = lev::Index::getCurrentIndex();
}
std::string previewSubPath = makePreviewPath(levelProxy);
Surface *surface = 0;
// load preview from file bundled with the level itself
std::string absLevelPath ;
std::auto_ptr<std::istream> isptr;
ByteVec imageData;
if(levelProxy->getNormPathType() == lev::Proxy::pt_resource &&
app.resourceFS->findFile ("levels/" + levelProxy->getNormLevelPath() + ".png",
absLevelPath, isptr)) {
// load plain image file or zipped image
if (isptr.get() != NULL) {
// zipped file
Readfile (*isptr, imageData);
} else {
// plain file
basic_ifstream<char> ifs(absLevelPath.c_str(), ios::binary | ios::in);
Readfile (ifs, imageData);
}
SDL_RWops *imageHandle = SDL_RWFromMem(&(imageData[0]), imageData.size());
surface = ecl::LoadImage(imageHandle, 1);
imgCache.store(previewSubPath, surface); // insert in imgCache
}
// load preview from stored file if possible
string previewFullPath;
if (!surface && app.resourceFS->findFile(previewSubPath, previewFullPath))
surface = imgCache.get(previewFullPath);
// generate new preview otherwise
if (!surface && allowGeneration) {
surface = newPreview(levelProxy);
if (surface) {
imgCache.store(previewSubPath, surface); // insert in imgCache
savePreview(levelProxy, surface); // save on filesystem
} else {
surface = enigma::GetImage("error");
}
didGenerate = true;
}
// update index
if (surface)
cache[mapIndex] = surface;
return surface;
}
ecl::Surface *LevelPreviewCache::newPreview (lev::Proxy *levelProxy) {
const video::VMInfo &vminfo = *video::GetInfo();
Surface *surface = 0;
ecl::GC gc(video::BackBuffer());
if (game::DrawLevelPreview (gc, levelProxy)) {
surface = Resample (video::BackBuffer(),
vminfo.gamearea, vminfo.thumbw, vminfo.thumbh);
}
return surface;
}
Surface *LevelPreviewCache::updatePreview (lev::Proxy *levelProxy) {
if (Surface *surface = newPreview (levelProxy)) {
std::string idx = levelProxy->getId() +
ecl::strf("#%d", levelProxy->getReleaseVersion());
cache[idx] = surface;
string preview_name = makePreviewPath(levelProxy);
imgCache.store (preview_name, surface); // insert in imgCache
savePreview(levelProxy, surface); // save on filesystem
return surface;
}
return 0;
}
std::string LevelPreviewCache::makePreviewPath(lev::Proxy *levelProxy) {
return "thumbs/" +
levelProxy->getLocalSubstitutionLevelPath() +
ecl::strf("#%d", levelProxy->getReleaseVersion()) + ".png";
}
void LevelPreviewCache::savePreview(lev::Proxy *levelProxy, ecl::Surface *s) {
std::string savePath = app.userImagePath + "/" + makePreviewPath(levelProxy);
Log << "savePreview to " << savePath << "\n";
// auto-create the directory if necessary
string directory;
if (ecl::split_path (savePath, &directory, 0) && !ecl::FolderExists(directory)) {
ecl::FolderCreate (directory);
}
ecl::SavePNG(s, savePath);
}
void LevelPreviewCache::makeSystemPreview(lev::Proxy *levelProxy, std::string systemDataPath) {
std::string savePath = systemDataPath + "/levels/" + levelProxy->getNormLevelPath() + ".png";
// auto-create the directory if necessary -- on an installed Enigma
// distribution this is of course unnecessary, but you start Enigma
// without prior installation. This is useful to get a directory with
// just the previews.
string directory;
if (ecl::split_path (savePath, &directory, 0) && !ecl::FolderExists(directory)) {
ecl::FolderCreate (directory);
}
ecl::Surface * s = newPreview(levelProxy);
if (s != NULL)
ecl::SavePNG(s, savePath);
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_LEVELPREVIEWCACHE_HH_INCLUDED
#define GUI_LEVELPREVIEWCACHE_HH_INCLUDED
#include "lev/Index.hh"
#include "lev/Proxy.hh"
#include <map>
namespace enigma { namespace gui {
/**
* A singleton manager for level previews with autocaching.
* Clients just need to know the level proxy to request a preview.
* <p>
* Previews will be loaded from levelpack bundles if provided. These
* preview files have the same name as the levels themself but the
* suffix ".png". They just sit aside the levels in the same directory
* or in the same zip archive. Only local stored previews will be looked
* for.<p>
* If no bundled preview exists the preview will be loaded from prior
* generated and saved instances. Previews will be looked up at the
* resourceFS with play time generated previews stored at the userImagePath
* and installation time generated previews at the system path. The previews
* will be stored at "data/thumbs" with a subpath that reflectes the level
* subpath with critical characters replaced for url and other special levels.
* The level release number is attached to the previewname as "#n" to allow
* different previews for different releases to exist in parallel.<p>
*
* If no stored preview exists a new one will be generated by loading the
* level. The preview will be stored to the userImagePath for future use.<p>
*
* All loaded previews will be autocached. Futher requests will be served
* by the cache. The cache will be autocleared when a change in the
* current Index is detected.<p>
*
* TODO remove unused preview on filesystem
* TODO autogenerate previews on install
*/
class LevelPreviewCache {
public:
static LevelPreviewCache *instance();
static void makeSystemPreview(lev::Proxy *levelProxy, std::string systemDataPath);
~LevelPreviewCache();
ecl::Surface *getPreview (lev::Proxy *levelProxy,
bool allowGeneration, bool &didGenerate);
ecl::Surface *updatePreview (lev::Proxy *levelProxy);
void clear();
protected:
LevelPreviewCache();
private:
static LevelPreviewCache *theSingleton;
/**
* A mapping of "levelId+levelRelease" to preview surfaces
*/
typedef std::map<std::string, ecl::Surface*> PreviewMap;
// ---------- Internal methods ----------
static ecl::Surface *newPreview (lev::Proxy *levelProxy);
std::string makePreviewPath(lev::Proxy *levelProxy);
void savePreview(lev::Proxy *levelProxy, ecl::Surface *s);
// ---------- Variables ----------
PreviewMap cache; // a second mapping to avoid searched on the filesystem
enigma::ImageCache imgCache; // the owner of the preview surfaces -
// cannot be used as mapping as this cache
// uses the filepath as index and autoloads
// files on "get"-access
lev::Index *cachedIndex; // the index that is currently cached
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,489 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
* 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 "gui/LevelWidget.hh"
#include "gui/LevelMenu.hh"
#include "gui/LevelInspector.hh"
#include "ecl.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "sound.hh"
#include "StateManager.hh"
#include "video.hh"
#include "file.hh"
#include "lev/Proxy.hh"
#include <cassert>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- LevelWidget -------------------- */
LevelWidget::LevelWidget(bool withScoreIcons, bool withEditBorder) :
displayScoreIcons (withScoreIcons), displayEditBorder (withEditBorder),
width (0), height (0), m_areas(),
listener(0), isInvalidateUptodate (true), lastUpdate (0)
{
const video::VMInfo &vminfo = *video::GetInfo();
buttonw = vminfo.thumbw + 20;
buttonh = vminfo.thumbh + 28;
curIndex = lev::Index::getCurrentIndex();
iselected = curIndex->getCurrentPosition();
ifirst = curIndex->getScreenFirstPosition();
preview_cache = LevelPreviewCache::instance();
scoreMgr = lev::ScoreManager::instance();
img_link = enigma::GetImage("ic-link");
img_copy = enigma::GetImage("ic-copy");
img_feather = enigma::GetImage("ic-feather");
img_easy = enigma::GetImage("completed-easy");
img_hard = enigma::GetImage("completed");
img_changed = enigma::GetImage("changed");
img_unavailable = enigma::GetImage("unavailable");
// img_unknown = enigma::GetImage("unknown");
img_par = enigma::GetImage("par");
img_wrEasy = enigma::GetImage("ic-wr-easy");
img_wrDifficult = enigma::GetImage("ic-wr-difficult");
img_border = enigma::GetImage("thumbborder");
img_editborder = enigma::GetImage("editborder");
}
void LevelWidget::syncFromIndexMgr() {
if (curIndex != lev::Index::getCurrentIndex()) {
curIndex = lev::Index::getCurrentIndex();
iselected = curIndex->getCurrentPosition();
ifirst = curIndex->getScreenFirstPosition();
invalidate();
sound::EmitSoundEvent ("menumove");
} else if (iselected != curIndex->getCurrentPosition()) {
sound::EmitSoundEvent ("menumove");
}
// repair ifirst on boot and screen resolution changes
set_selected(curIndex->getScreenFirstPosition(),
curIndex->getCurrentPosition());
}
void LevelWidget::syncToIndexMgr() {
curIndex->setCurrentPosition(iselected);
curIndex->setScreenFirstPosition(ifirst);
}
void LevelWidget::realize (const ecl::Rect &area_)
{
Widget::realize (area_);
width = area_.w / buttonw;
height = area_.h / buttonh;
}
void LevelWidget::trigger_action() {
if (listener) {
listener->on_action(this);
}
}
void LevelWidget::scroll_up (int nlines)
{
int newFirst = ifirst;
int newSelected = iselected;
for (; nlines; --nlines) {
if (newFirst+width*height >= curIndex->size())
break;
newFirst += width;
if (newSelected < newFirst)
newSelected += width;
}
set_selected (newFirst, newSelected);
}
void LevelWidget::scroll_down(int nlines)
{
int newFirst = ifirst;
int newSelected = iselected;
for (; nlines; --nlines) {
if (newFirst == 0) {
break;
} else if (newFirst < width) {
newFirst = 0;
if (newSelected >= width*height)
newSelected = width*height - 1;
} else {
newFirst -= width;
if (newSelected >= newFirst+width*height)
newSelected -= width;
}
}
set_selected (newFirst, newSelected);
}
void LevelWidget::page_up() {
set_selected ((ifirst >= width*height ? ifirst - width*height : 0),
(iselected >= width*height ? iselected - width*height : 0));
syncToIndexMgr();
}
void LevelWidget::page_down()
{
int s = lev::Index::getCurrentIndex()->size() - 1; // last position
int lastPageFirst = (s >= width*height ? (s / width - height + 1) * width : 0);
// make sure last page is shown as a whole
int first = std::min<int> (lastPageFirst, ifirst + width*height);
// set_selected (first, s-1);
set_selected (first, iselected + width*height);
syncToIndexMgr();
}
void LevelWidget::start() {
set_selected(0,0);
syncToIndexMgr();
}
void LevelWidget::end() {
int s = lev::Index::getCurrentIndex()->size() - 1; // last position
int lastPageFirst = (s >= width*height ? (s / width - height + 1) * width : 0);
set_selected(lastPageFirst, s);
syncToIndexMgr();
}
void LevelWidget::set_current (int newsel)
{
set_selected (ifirst, newsel);
}
void LevelWidget::set_selected (int newfirst, int newsel)
{
int numlevels = curIndex->size();
newsel = Clamp<int> (newsel, 0, numlevels-1);
if (newsel < 0) newsel = 0;
if (newsel < newfirst)
newfirst = (newsel/width)*width;
if (newsel >= newfirst+width*height)
newfirst = (newsel/width-height+1)*width;
newfirst = Clamp<int> (newfirst, 0, numlevels-1);
if (newfirst < 0) newfirst = 0;
size_t oldsel = iselected;
if (newfirst != ifirst) {
ifirst = newfirst;
iselected = newsel;
if (!m_areas.empty()) {
sound::EmitSoundEvent ("menumove");
if (oldsel != newsel)
sound::EmitSoundEvent ("menuswitch");
invalidate();
}
}
else if (newsel != iselected) {
iselected = newsel;
if (!m_areas.empty()) {
sound::EmitSoundEvent ("menuswitch");
invalidate_area(m_areas[oldsel-ifirst]); // old selection
invalidate_area(m_areas[iselected-ifirst]); // new selection
}
}
}
bool LevelWidget::draw_level_preview (ecl::GC &gc, int x, int y,
lev::Proxy *proxy, bool selected, bool isCross, bool locked,
bool allowGeneration, bool &didGenerate) {
// Draw button with level preview
Surface *img = preview_cache->getPreview(proxy, allowGeneration, didGenerate);
if (img == NULL)
return false;
if (selected) {
blit (gc, x-4, y-4, displayEditBorder ? img_editborder : img_border);
blit (gc, x, y, img);
}
else {
img->set_alpha (127);
blit (gc, x, y, img);
img->set_alpha(255);
}
// Shade unavailable levels
if (locked)
blit (gc, x, y, img_unavailable);
if (displayScoreIcons) {
// Draw solved/changed icons on top of level preview
// Easy/difficult mode and solved status:
// None: Level not beaten - no easy mode available
// Feather: Level not beaten - easy mode available
// Feather + Gold: Level beaten in normal mode - easy available
// Silver: Level beaten in easy mode (normal mode available)
// Gold: Level beaten in normal mode - easy not availabe
// Silver + Gold: Level beaten in all modes - easy available
Surface *useAsEasy = NULL;
Surface *useAsDifficult = NULL;
if (proxy->hasEasymode()) {
useAsEasy = img_feather;
if (scoreMgr->isSolved(proxy, DIFFICULTY_EASY))
useAsEasy = img_easy;
}
if (scoreMgr->isSolved(proxy, DIFFICULTY_HARD))
useAsDifficult = img_hard;
if (app.state->getInt("Difficulty") == DIFFICULTY_HARD) {
// draw golden medal over silber medal
if (useAsEasy != NULL)
blit (gc, x, y, useAsEasy);
if (useAsDifficult != NULL)
blit (gc, x+5, y, useAsDifficult);
}
else {
// draw silver medal over golden medal
if (useAsDifficult != NULL)
blit (gc, x+5, y, useAsDifficult);
if (useAsEasy != NULL)
blit (gc, x, y, useAsEasy);
}
// Add warning sign if level has been changed since player solved it
if (scoreMgr->isOutdated(proxy, app.state->getInt("Difficulty")))
blit (gc, x-3, y-3, img_changed);
// Add icon if worldrecord or par
if (scoreMgr->bestScoreReached(proxy, app.state->getInt("Difficulty"))) {
blit(gc, x+35, y+5,
(app.state->getInt("Difficulty") != DIFFICULTY_HARD &&
proxy->hasEasymode()) ? img_wrEasy : img_wrDifficult);
} else if (scoreMgr->parScoreReached(proxy, app.state->getInt("Difficulty"))){
blit(gc, x+30, y+12, img_par);
}
} else {
// Draw solved/changed icons on top of level preview
if (isCross)
blit (gc, x+4, y+4, img_link);
else
blit (gc, x+4, y+4, img_copy);
}
return true;
}
void LevelWidget::draw (ecl::GC &gc, const ecl::Rect &r)
{
const video::VMInfo &vminfo = *video::GetInfo();
const int imgw = vminfo.thumbw; // Size of the preview images
const int imgh = vminfo.thumbh;
const int hgap = Max(0, (get_w() - width*buttonw) / (width));
const int vgap = Max(0, (get_h() - height*buttonh)/ (height-1));
unsigned i=ifirst; // level index
bool allowGeneration = true;
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++, i++)
{
if (i >= curIndex->size())
goto done_painting;
int xpos = get_x() + hgap/2 + x*(buttonw + hgap);
int ypos = get_y() + y*(buttonh + vgap);
Rect buttonarea(xpos, ypos, buttonw, buttonh);
if (!(r.overlaps(buttonarea) || r.w == 0))
continue; // r.w==0 if repainting whole screen
if( (i-ifirst) >= m_areas.size()) {
m_areas.push_back(buttonarea);
pending_redraws.push_back(false);
} else {
m_areas[(i-ifirst)] = buttonarea;
}
// Draw level preview
lev::Proxy *levelProxy = curIndex->getProxy(i);
int imgx = xpos+(buttonw-imgw)/2;
int imgy = ypos + 4;
if (levelProxy != NULL) {
bool didGenerate;
bool didDraw = draw_level_preview (gc, imgx, imgy, levelProxy,
i == iselected, !curIndex->isSource(levelProxy),
!curIndex->mayPlayLevel(i+1),
allowGeneration, didGenerate);
if (didGenerate) {
// do not generate more than 1 preview from level source
// per draw call
allowGeneration = false;
}
if (didDraw) {
pending_redraws[(i-ifirst)] = false;
} else {
// the button is not drawn - mark it to be drawn on
// a future tick
pending_redraws[(i-ifirst)] = true;
isInvalidateUptodate = false;
}
}
// Draw level name
Font *smallfnt = enigma::GetFont("levelmenu");
Font *altsmallfnt = enigma::GetFont("smallalternative");;
std::string caption = levelProxy->getTitle();
smallfnt->render (gc,
xpos + buttonw/2 - ecl::Min(smallfnt->get_width(caption.c_str(), altsmallfnt)/2, (buttonw+hgap)/2),
imgy + imgh + 2,
caption, altsmallfnt, buttonw + hgap);
}
}
done_painting:
m_areas.resize (i-ifirst); // Remove unused areas (if any) from the list
return;
}
void LevelWidget::tick (double time) {
if (!isInvalidateUptodate) {
// invalidate just 1 button for redraw
bool isFirst = true;
for (int i = 0; i < pending_redraws.size(); i++) {
if (pending_redraws[i] == true) {
if (isFirst) {
invalidate_area(m_areas[i]);
isInvalidateUptodate = true;
isFirst = false;
} else {
isInvalidateUptodate = false;
return;
}
}
}
}
}
bool LevelWidget::on_event(const SDL_Event &e)
{
bool handled = Widget::on_event(e);
switch (e.type) {
case SDL_MOUSEMOTION:
if (get_area().contains(e.motion.x, e.motion.y)) {
int newsel=iselected;
for (unsigned i=0; i<m_areas.size(); ++i)
if (m_areas[i].contains(e.motion.x, e.motion.y))
{
newsel = ifirst+i;
break;
}
set_current(newsel);
handled = true;
}
break;
case SDL_MOUSEBUTTONDOWN:
if (get_area().contains(e.button.x, e.button.y))
handled = handle_mousedown (&e);
break;
case SDL_KEYDOWN:
handled = handle_keydown (&e);
break;
}
syncToIndexMgr();
return handled;
}
bool LevelWidget::handle_mousedown (const SDL_Event *e)
{
switch (e->button.button) {
case SDL_BUTTON_LEFT:
for (unsigned i=0; i<m_areas.size(); ++i)
if (m_areas[i].contains(e->button.x, e->button.y))
{
sound::EmitSoundEvent ("menuok");
iselected = ifirst+i;
syncToIndexMgr();
if (SDL_GetModState() & KMOD_CTRL && !(SDL_GetModState() & KMOD_SHIFT)) {
// control key pressed - level inspector
LevelInspector m(curIndex->getProxy(iselected));
m.manage();
get_parent()->draw_all();
} else {
// no control key - start level
trigger_action();
}
return true;
}
break;
case SDL_BUTTON_RIGHT:
for (unsigned i=0; i<m_areas.size(); ++i)
if (m_areas[i].contains(e->button.x, e->button.y))
{
sound::EmitSoundEvent ("menuok");
iselected = ifirst+i;
syncToIndexMgr();
LevelInspector m(curIndex->getProxy(iselected));
m.manage();
get_parent()->draw_all();
return true;
}
break;
case 4: scroll_down(1); return true;
case 5: scroll_up(1); return true;
}
return false;
}
bool LevelWidget::handle_keydown (const SDL_Event *e)
{
switch (e->key.keysym.sym) {
case SDLK_t:
// Generate new level preview for current level
preview_cache->updatePreview(curIndex->getProxy(iselected));
invalidate();
break;
case SDLK_LEFT:
if (!(SDL_GetModState() & KMOD_ALT)) {
set_current (iselected>1 ? iselected-1 : 0);
break;
} else
return false;
case SDLK_RIGHT:
if (!(SDL_GetModState() & KMOD_ALT)) {
set_current (iselected+1);
break;
} else
return false;
case SDLK_DOWN: set_current (iselected+width); break;
case SDLK_UP: set_current (iselected>width ? iselected-width : 0); break;
case SDLK_PAGEDOWN: page_down(); break;
case SDLK_PAGEUP: page_up(); break;
case SDLK_HOME: start(); break;
case SDLK_END: end(); break;
case SDLK_RETURN:
trigger_action();
break;
default:
return false; // key not handled
}
return true;
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_LEVELWIDGET_HH_INCLUDED
#define GUI_LEVELWIDGET_HH_INCLUDED
#include "gui/widgets.hh"
#include "gui/LevelPreviewCache.hh"
#include "lev/Index.hh"
#include "lev/ScoreManager.hh"
namespace enigma { namespace gui {
/* -------------------- LevelWidget -------------------- */
class LevelMenu;
/**
*
*/
class LevelWidget : public Widget {
public:
LevelWidget(bool withScoreIcons = true, bool withEditBorder = false);
//---------- Widget interface ----------//
void draw(ecl::GC &gc, const ecl::Rect &r);
void tick (double time);
void set_listener(ActionListener *al) {
listener = al;
}
void trigger_action();
virtual void realize (const ecl::Rect &r);
//---------- Cursor motion ----------//
void page_up();
void page_down();
void start();
void end();
bool on_event(const SDL_Event &e);
void syncFromIndexMgr(); // external change of currentIndex, currentLevel
private:
//---------- Private functions ----------//
void syncToIndexMgr();
void set_current (int newsel);
void scroll_up(int lines);
void scroll_down(int lines);
void set_selected (int newfirst, int newsel);
bool draw_level_preview (ecl::GC &gc, int x, int y,
lev::Proxy *proxy, bool selected, bool isCross, bool locked,
bool allowGeneration, bool &didGenerate);
bool handle_keydown (const SDL_Event *e);
bool handle_mousedown (const SDL_Event *e);
//---------- Variables ----------//
bool displayScoreIcons;
bool displayEditBorder;
LevelPreviewCache *preview_cache;
lev::ScoreManager *scoreMgr;
lev::Index *curIndex;
ActionListener *listener;
int ifirst; // Index of "upper left" level
int iselected; // Index of selected level
int width; // number of buttons in a row
int height; // number of buttons in a column
int buttonw; // pixelwidth of a button
int buttonh; // pixelheight of a button
std::vector<ecl::Rect> m_areas; // Screen areas occupied by level previews
std::vector<bool> pending_redraws;
bool isInvalidateUptodate;
double lastUpdate;
// some image pointers for efficiency
ecl::Surface *img_link;
ecl::Surface *img_copy;
ecl::Surface *img_feather;
ecl::Surface *img_easy;
ecl::Surface *img_hard;
ecl::Surface *img_changed;
ecl::Surface *img_unavailable;
// Surface *img_unknown;
ecl::Surface *img_par;
ecl::Surface *img_wrEasy;
ecl::Surface *img_wrDifficult;
ecl::Surface *img_border;
ecl::Surface *img_editborder;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,392 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 "gui/MainMenu.hh"
#include "gui/LevelMenu.hh"
#include "gui/OptionsMenu.hh"
#include "gui/HelpMenu.hh"
#include "gui/InfoMenu.hh"
#include "gui/LevelPackMenu.hh"
#include "gui/LevelPreviewCache.hh"
#include "display.hh"
#include "ecl.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "sound.hh"
#include "video.hh"
#include "world.hh"
#include "netgame.hh"
#include "editor.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- Helper routines -------------------- */
// namespace
// {
/*! Change the video mode. Because this opens a new screen with a
new resolution, the display engine must be re-initialized to
load the appropriate models. */
void ChangeVideoMode()
{
world::PrepareLevel(); // make sure no references to models remain
video::ChangeVideoMode();
LevelPreviewCache::instance()->clear();
enigma::ClearImageCache();
display::Shutdown();
display::Init();
}
// }
/* -------------------- NetworkMenu -------------------- */
NetworkMenu::NetworkMenu ()
{
const video::VMInfo *vminfo = video::GetInfo();
BuildVList b(this, Rect((vminfo->width - 150)/2,150,150,40), 5);
m_startgame = b.add(new StaticTextButton(N_("Start Game"), this));
m_joingame = b.add(new StaticTextButton(N_("Join Game"), this));
m_back = b.add(new StaticTextButton(N_("Back"), this));
}
NetworkMenu::~NetworkMenu ()
{
}
bool NetworkMenu::on_event (const SDL_Event &e)
{
return false;
}
void NetworkMenu::on_action(gui::Widget *w)
{
if (w == m_startgame) {
netgame::Start();
}
else if (w == m_joingame) {
netgame::Join("localhost", 12345);
}
if (w == m_back)
Menu::quit();
}
void NetworkMenu::draw_background(ecl::GC &gc)
{
video::SetCaption (("Enigma - Network Menu"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
void NetworkMenu::tick(double dtime)
{
}
/* -------------------- Main menu -------------------- */
static const char *credit_text[] = {
N_("Main developers of the 1.0 release:"),
N_(" RONALD LAMPRECHT (lead)"),
" RAOUL BOURQUIN",
" ANDREAS LOCHMANN",
" ",
N_("Special Thanks To:"),
N_(" DANIEL HECK (project founder)"),
N_(" MEINOLF SCHNEIDER (game idea, level design)"),
" ",
N_("Please refer to the manual or the next pages for full credits."),
" ",
N_("Home Page: http://www.nongnu.org/enigma"),
N_("Contact: enigma-devel@nongnu.org"),
" ",
N_("Enigma is free software and may be distributed under the"),
N_("terms of the GNU General Public License, version 2."),
" ",
N_("Copyright (C) 2002-2007 Daniel Heck and contributors."),
0,
N_("Main developer of all releases:"),
" ",
" Raoul Bourquin (Level admin. & design, all over invaluable additions)",
" Siegfried Fennig (Level design, graphics)",
" Martin Hawlisch (Level design, graphics, programming)",
" Daniel Heck (Main developer, graphics, documentation)",
" Ronald Lamprecht (XML, Gui, portability, core prog., documentation)",
" Andreas Lochmann (Programming, level admin. & design, documentation)",
" Petr Machata (Level design, programming)",
" Nat Pryce (Level design)",
" Jacob Scott (Level design)",
" Sven Siggelkow (Level design and special Oxyd expertise)",
" Ralf Westram (Programming, level design)",
0,
N_("Special Thanks:"),
" Johannes Fortmann (Mac OS X port, some programming, graphics)",
" illmind (Forum mag-heut.net administration, Level design)",
// waiting for licence to add the sounds
// " \'Cellar of Rats\' (Sound effects)",
" Jennifer Robertson (Graphics second generation)",
" Jeremy Sawicki (Oxydlib)",
" Erich Schubert (Debian/Ubuntu packages, level design)",
" Lukas Schüller (Level design)",
" Andrew \'Necros\' Sega (Menu music \'Pentagonal Dreams\')",
" David W. Skinner (Many Sokoban Levels)",
" Clifford J. Tasner (Music second generation, Proof reading)",
0,
N_("Contributors:"),
" Roberto Bardin (Level design)",
" Alain Busser (Level design, French translation, manual)",
" Guy Busser (Level design)",
" Richi Bützer (Level design)",
" Capkoh (Level design)",
" Christoph & Anita (Level design)",
" Дремук Сергей - Serge Dremuk (Russian translation)",
" Joseph Dunne (Level design)",
" Xerxes M. Dynatos (Level design)",
" Edward (Level design)",
" Stephanie Fabian (Invaluable bug reports)",
" Roberto García (Spanish translation)",
" Andy Geldmacher (Level design)",
" Edwin Groothuis (FreeBSD port)",
" Immanuel Herrmann (Level design)",
" Máté Lehel Juhász (Hungarian translation)",
0,
" Samuele Kaplun (Italian translation)",
" Jens-Christian Korth (Level design)",
" Manuel König (Level design, bug reports)",
" Johannes Laire (Level design)",
" Joona Laire (Level design)",
" Markus Laire (Level design)",
" Dominik Lehmann (Level design)",
" Edward Leuf (Feedback, bug reports)",
" Ingo van Lil (Feedback, bug reports)",
" Frank van der Loo (Dutch translation)",
" Sidney Markowitz (Mac OS X port)",
" Barry & Lori Mead (Level design)",
" Linda Mihalic (English proof reading)",
" moonpearl (Level design)",
" Krishnamurti L.L.V. Nunes (Portuguese translation)",
" Daniel Nylander (Swedish translation)",
0,
" Andreas Persenius (Level design)",
" Peter Santo (Level design)",
" Tobias Schmidbauer (Windows installer and icon)",
" Achim Settelmeier (RPM specfile)",
" Jon Sneyers (Level design)",
" Spaceman (Level design)",
" Ulf Stegemann (Level design)",
" Jürgen Sticht (Level design)",
" Mikke Surakka (Finnish translation)",
" Andrzej Szombierski (Level design)",
" Tacvek (Lua 5.1 upgrade)",
" Michael Terry (.desktop file)",
" Ray Wick (Level design)",
" Joe Wreschnig (Manual page)",
" Юрий Жиромский - Yuriy Zhyromskiy (Russian Manual)",
0,
};
MainMenu::MainMenu()
{
build_menu();
}
void MainMenu::build_menu()
{
const video::VMInfo *vminfo = video::GetInfo();
BuildVList b(this, Rect((vminfo->width - 150)/2,150,150,40), 5);
m_startgame = b.add(new StaticTextButton(N_("Start Game"), this));
m_levelpack = b.add(new StaticTextButton(N_("Level Pack"), this));
#ifdef ENABLE_EXPERIMENTAL
m_netgame = b.add (new StaticTextButton (N_("Network Game"), this));
leveled = b.add(new StaticTextButton(N_("Editor"), this));
#endif
options = b.add(new StaticTextButton(N_("Options"), this));
credits = b.add(new StaticTextButton(N_("Credits"), this));
quit = b.add(new StaticTextButton(N_("Quit"), this));
}
void MainMenu::draw_background(ecl::GC &gc)
{
const video::VMInfo *vminfo = video::GetInfo();
video::SetCaption (("Enigma - Main Menu"));
sound::PlayMusic (options::GetString("MenuMusicFile"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("levelmenu");
Surface * logo(enigma::GetImage("enigma_logo3"));
int x0=(vminfo->width - logo->width())/2;
int y0=30;
blit(gc, x0, y0, logo);
f->render (gc, 5, vminfo->height - 20, app.getVersionInfo().c_str());
}
bool MainMenu::on_event (const SDL_Event &e) {
switch (e.type) {
case SDL_KEYDOWN:
SDLKey keysym = e.key.keysym.sym;
switch (keysym) {
case SDLK_F2:
show_paths();
invalidate_all();
return true;
default:
break;
}
break;
}
return false;
}
void MainMenu::on_action(Widget *w)
{
if (w == m_startgame) {
LevelPackMenu m;
m.manageLevelMenu();
invalidate_all();
} else if (w == m_levelpack) {
LevelPackMenu m;
m.manage();
invalidate_all();
} else if (w == credits) {
displayInfo(credit_text, 6);
invalidate_all();
} else if (w == options) {
ShowOptionsMenu(0);
#ifdef ENABLE_EXPERIMENTAL
} else if (w == m_netgame) {
ShowNetworkMenu();
} else if (w == leveled) {
editor::Run();
#endif
} else if (w == quit) {
Menu::quit();
} else
return;
invalidate_all();
}
void MainMenu::tick(double /* dtime */)
{
bool option_fullscreen = options::GetInt ("FullScreen") != 0;
if (options::GetInt ("VideoMode") != video::GetVideoMode()
|| option_fullscreen != video::IsFullScreen())
{
ChangeVideoMode();
clear();
reset_active_widget ();
build_menu();
invalidate_all();
}
}
void MainMenu::show_paths() {
const char *pathtext[25];
std::string pathstrings[25];
std::string work;
Font *menufont = enigma::GetFont("menufont");
const video::VMInfo *vminfo = video::GetInfo();
int width = vminfo->width - 80;
int i = 0;
pathtext[i++] = N_("Preferences Path:");
work = app.prefPath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("User Path:");
work = app.userPath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("User Image Path:");
work = app.userImagePath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("System Path:");
work = app.systemFS->getDataPath();
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("Resource Paths:");
work = app.resourceFS->getDataPath();
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = " ";
pathtext[i++] = N_("L10n Path:");
work = app.l10nPath;
do {
std::string::size_type breakPos = breakString(menufont, work,"/\\", width);
pathstrings[i] = " " + work.substr(0,breakPos);
pathtext[i] = pathstrings[i].c_str();
work = work.substr(breakPos);
i++;
} while(!work.empty() );
pathtext[i++] = 0;
displayInfo(pathtext, 1);
}
/* -------------------- Functions -------------------- */
void ShowMainMenu() {
MainMenu m;
m.manage();
}
void ShowNetworkMenu()
{
NetworkMenu m;
m.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_MAINMENU_HH_INCLUDED
#define GUI_MAINMENU_HH_INCLUDED
#include "gui/Menu.hh"
namespace enigma { namespace gui {
/* -------------------- MainMenu -------------------- */
class MainMenu : public Menu {
public:
MainMenu();
private:
// Menu interface
void draw_background(ecl::GC &gc);
void tick(double dtime);
// Widget interface
virtual bool on_event(const SDL_Event &e);
// ActionListener interface.
void on_action(Widget *w);
// Private methods.
void show_paths();
void build_menu();
// Variables.
Widget *m_startgame;
Widget *m_levelpack;
Widget *m_netgame;
Widget *leveled;
Widget *manual;
Widget *options;
Widget *credits;
Widget *quit;
Widget *lpack;
};
/* -------------------- NetworkMenu -------------------- */
class NetworkMenu : public gui::Menu {
public:
NetworkMenu ();
~NetworkMenu ();
private:
// ActionListener interface.
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
// Menu interface.
void draw_background(ecl::GC &gc);
void tick(double dtime);
// Variables.
gui::Widget *m_startgame;
gui::Widget *m_joingame;
gui::Widget *m_back;
};
/* -------------------- Functions -------------------- */
void ShowMainMenu();
void ShowNetworkMenu();
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 "gui/Menu.hh"
#include "sound.hh"
#include "video.hh"
#include "options.hh"
#include "nls.hh"
#include "ecl.hh"
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace ecl;
using namespace std;
#define SCREEN ecl::Screen::get_instance()
namespace enigma { namespace gui {
/* -------------------- Menu -------------------- */
Menu::Menu()
: active_widget(0), quitp(false), abortp(false) {
}
void Menu::add(Widget *w) {
Container::add_child(w);
}
void Menu::add(Widget *w, ecl::Rect r) {
w->move (r.x, r.y);
w->resize (r.w, r.h);
add(w);
}
void Menu::quit() {
quitp=true;
}
void Menu::abort() {
abortp=true;
}
bool Menu::manage() {
quitp=abortp=false;
SDL_Event e;
Uint32 enterTickTime = SDL_GetTicks(); // protection against ESC D.o.S. attacks
while (SDL_PollEvent(&e)) {} // clear event queue
draw_all();
while (!(quitp || abortp)) {
SCREEN->flush_updates();
while (SDL_PollEvent(&e)) {
handle_event(e);
}
SDL_Delay(10);
if(active_widget) active_widget->tick(0.01);
tick (0.01);
refresh();
}
sound::EmitSoundEvent ("menuexit");
// protection against ESC D.o.S. attacks
Uint32 menuTickDuration = SDL_GetTicks() - enterTickTime;
Uint32 minMenuTickDuration = 300;
if (menuTickDuration < minMenuTickDuration)
SDL_Delay(minMenuTickDuration - menuTickDuration);
while (SDL_PollEvent(&e)) {} // clear event queue
return !abortp;
}
void Menu::goto_adjacent_widget(int xdir, int ydir) {
Widget *next_widget = 0;
if (active_widget) {
next_widget = find_adjacent_widget(active_widget, xdir, ydir);
}
else { // no active_widget yet
if ((xdir+ydir)>0) { // take first
next_widget = *begin();
}
else { // take last
iterator e = end();
for (iterator i = begin(); i != e; ++i) {
next_widget = *i;
}
}
}
if (next_widget) {
switch_active_widget(next_widget);
}
else { // no more widgets into that direction found
sound::EmitSoundEvent ("menustop");
}
}
void Menu::handle_event(const SDL_Event &e)
{
if (e.type == SDL_KEYDOWN &&
e.key.keysym.sym == SDLK_RETURN &&
e.key.keysym.mod & KMOD_ALT)
{
video::ToggleFullscreen();
return;
}
if (on_event(e))
return;
switch (e.type) {
case SDL_QUIT:
abort();
break;
case SDL_MOUSEMOTION:
track_active_widget( e.motion.x, e.motion.y );
break;
case SDL_KEYDOWN:
if (!active_widget || !active_widget->on_event(e)) {
// if not handled by active_widget
switch (e.key.keysym.sym) {
case SDLK_ESCAPE:
abort();
break;
case SDLK_DOWN: goto_adjacent_widget( 0, 1); break;
case SDLK_UP: goto_adjacent_widget( 0, -1); break;
case SDLK_RIGHT: goto_adjacent_widget( 1, 0); break;
case SDLK_LEFT: goto_adjacent_widget(-1, 0); break;
default:
break;
}
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
track_active_widget( e.button.x, e.button.y );
if (active_widget) active_widget->on_event(e);
break;
case SDL_VIDEOEXPOSE:
draw_all();
break;
default:
if (active_widget) active_widget->on_event(e);
}
}
void Menu::switch_active_widget(Widget *to_activate) {
if (to_activate != active_widget) {
if (active_widget)
active_widget->deactivate();
if (to_activate)
to_activate->activate();
active_widget = to_activate;
}
}
void Menu::track_active_widget( int x, int y ) {
switch_active_widget(find_widget(x, y));
}
void Menu::center() {
if (m_widgets.size() > 0) {
using std::min;
using std::max;
Rect a = m_widgets[0]->get_area();
for (unsigned i=1; i<m_widgets.size(); ++i)
{
Rect r = m_widgets[i]->get_area();
a.x = min(r.x, a.x);
a.y = min(r.y, a.y);
a.w += max(0, r.x+r.w-a.x-a.w);
a.h += max(0, r.y+r.h-a.y-a.h);
}
Rect c=ecl::center(SCREEN->size(), a);
int dx = c.x-a.x;
int dy = c.y-a.y;
for (unsigned i=0; i<m_widgets.size(); ++i) {
Rect r = m_widgets[i]->get_area();
r.x += dx;
r.y += dy;
m_widgets[i]->move (r.x, r.y);
m_widgets[i]->resize (r.w, r.h);
}
}
}
void Menu::draw (ecl::GC &gc, const ecl::Rect &r)
{
clip(gc, r);
draw_background(gc);
Container::draw(gc,r);
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_MENU_HH_INCLUDED
#define GUI_MENU_HH_INCLUDED
#include "gui/widgets.hh"
#include "gui/Menu.hh"
#include "ecl_fwd.hh"
#include "ecl_geom.hh"
#include "SDL.h"
#include <cmath>
#include <vector>
namespace enigma { namespace gui {
/* -------------------- Menu -------------------- */
class Menu : public Container {
public:
Menu();
//! true: ok, false: menu aborted by user
virtual bool manage();
void add(Widget *w);
void add(Widget *w, ecl::Rect r);
void center();
void draw (ecl::GC &gc, const ecl::Rect &r);
virtual void quit();
void abort();
protected:
void reset_active_widget()
{ active_widget = 0; }
// Menu interface.
virtual void draw_background(ecl::GC &/*gc*/) {}
private:
void handle_event(const SDL_Event &e);
void switch_active_widget(Widget *to_activate);
void track_active_widget( int x, int y ); // used by mouse
void goto_adjacent_widget(int xdir, int ydir); // used by keyboard
// Variables.
Widget *active_widget;
bool quitp, abortp;
};
class BuildVList {
ecl::Rect r;
Menu *container;
int skip;
public:
BuildVList(Menu *cc, const ecl::Rect &rr, int s)
: r(rr), container(cc), skip(s)
{}
Widget *add(Widget *w) {
container->add(w, r);
r.y += r.h+skip;
return w;
}
ecl::Rect pos() const { return r; }
};
class BuildHList {
ecl::Rect r;
Menu *container;
int skip;
public:
BuildHList(Menu *cc, const ecl::Rect &rr, int s)
: r(rr), container(cc), skip(s)
{}
Widget * add(Widget *w) {
container->add(w, r);
r.x += r.w+skip;
return w;
}
Widget *add (Widget *w, int width) {
ecl::Rect rr(r.x, r.y, width, r.h);
container->add(w, rr);
r.x += width + skip;
return w;
}
ecl::Rect pos() const { return r; }
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,85 @@
/*
* 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 "gui/MonospacedLabel.hh"
#include "ecl.hh"
#include "nls.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
MonospacedLabel::MonospacedLabel (const std::string &text, char widthSample,
std::string monospaceCharacters, HAlignment halign, VAlignment valign) :
Label (text, halign, valign), sampleChar (widthSample),
monoChars (monospaceCharacters) {
}
void MonospacedLabel::draw (ecl::GC &gc, const ecl::Rect &) {
Font *f = m_font;
int w, h;
naturalsize (w, h);
int x = get_x(), y=get_y();
switch (m_halign) {
case HALIGN_LEFT: break;
case HALIGN_RIGHT: x += get_w() - w; break;
case HALIGN_CENTER: x += (get_w()-w)/2; break;
}
switch (m_valign) {
case VALIGN_TOP: break;
case VALIGN_BOTTOM: y += get_h() - h; break;
case VALIGN_CENTER: y += (get_h()-h)/2; break;
}
// translate if not an empty string
const char * translation = _(m_text.c_str());
int len = strlen(translation);
int monoWidth = m_font->get_width(sampleChar);
char c[] = " ";
for (int i = 0; i<len; i++) {
c[0] = translation[i];
if (monoChars.empty() || monoChars.find_first_of(c[0]) !=
std::string::npos) {
int charWidth = m_font->get_width(c[0]);
// centere char into monoWodth
f->render (gc, x + (monoWidth-charWidth)/2, y, c);
x += monoWidth;
} else {
f->render (gc, x, y, c);
x += m_font->get_width(c);
}
}
}
void MonospacedLabel::naturalsize (int &w, int &h) const {
h = m_font->get_height();
w = 0;
const char * translation = _(m_text.c_str());
int len = strlen(translation);
int monoWidth = m_font->get_width(sampleChar);
for (int i = 0; i<len; i++) {
if (monoChars.empty() || monoChars.find_first_of(translation[i]) !=
std::string::npos) {
w += monoWidth;
} else {
w += m_font->get_width(translation[i]);
}
}
}
}} // namespace enigma::lev

View File

@@ -0,0 +1,57 @@
/*
* 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 GUI_MONOSPACEDLABEL_HH_INCLUDED
#define GUI_MONOSPACEDLABEL_HH_INCLUDED
#include "gui/widgets.hh"
namespace enigma { namespace gui {
/**
* A text label that uses the same width for each character even for
* proportional fonts. Character positions of the label text is predictable
* and alignment of several labels is thus possible. Usefull for output of
* formatted numbers like score values.<p>
* Not all characters have to be monospaced. The set of characters that
* should have a constant width can be limited f.e. to the numbers 0-9.
*/
class MonospacedLabel : public Label {
public:
/**
* The standard constructor.
* @arg text The constant output text.
* @arg widthSample The character that defines the width. Default is 'm'.
* @arg monospaceCharacters The set of monospace characters. An empty
* string means all characters. Example "0123456789".
* @arg halign
* @arg valign
*/
MonospacedLabel (const std::string &text="", char widthSample = 'm',
std::string monospaceCharacters = "",
HAlignment halign=HALIGN_CENTER, VAlignment valign=VALIGN_CENTER);
// Widget interface
virtual void draw (ecl::GC &gc, const ecl::Rect &r);
virtual void naturalsize (int &w, int &h) const;
protected:
char sampleChar;
std::string monoChars;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,477 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 "gui/OptionsMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "lev/ScoreManager.hh"
#include "LocalToXML.hh"
#include "main.hh"
#include "nls.hh"
#include "options.hh"
#include "oxyd.hh"
#include "sound.hh"
#include "Utf8ToXML.hh"
#include "video.hh"
#include "XMLtoLocal.hh"
#include "XMLtoUtf8.hh"
#include <cassert>
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
/* -------------------- Buttons for Options -------------------- */
class MouseSpeedButton : public ValueButton {
int get_value() const {
return ecl::round_nearest<int>(options::GetMouseSpeed());
}
void set_value(int value) {
options::SetMouseSpeed (value);
}
string get_text(int value) const {
return strf("%d", value);
}
public:
MouseSpeedButton()
: ValueButton(options::MIN_MouseSpeed, options::MAX_MouseSpeed)
{ init(); }
};
class SoundVolumeButton : public ValueButton {
int get_value() const {
return round_nearest<int>(options::GetDouble("SoundVolume")*10.0);
}
void set_value(int value) {
options::SetOption("SoundVolume", value/10.0);
options::UpdateVolume();
}
string get_text(int value) const {
if (value == 0) {
return _("muted");
}
else {
return strf("%d", value);
}
}
public:
SoundVolumeButton() : ValueButton(0, 10) { init(); }
};
class MusicVolumeButton : public ValueButton {
int get_value() const {
return round_nearest<int> (options::GetDouble("MusicVolume")*10.0);
}
void set_value(int value) {
options::SetOption("MusicVolume", value/10.0);
options::UpdateVolume();
}
string get_text(int value) const {
if (value == 0)
return _("muted");
else
return strf("%d", value);
}
public:
MusicVolumeButton() : ValueButton(0, 10) { init(); }
};
class InGameMusicButton : public BoolOptionButton {
void on_action(Widget *) {
if (toggle())
sound::PlayMusic (options::GetString("LevelMusicFile"));
else
sound::StopMusic (options::GetString("LevelMusicFile"));
}
public:
InGameMusicButton() :
BoolOptionButton("InGameMusic", N_("Music in game"), N_("No music in game"), this)
{ }
};
struct SkipSolvedButton : public BoolOptionButton {
SkipSolvedButton() : BoolOptionButton("SkipSolvedLevels", N_("Yes"), N_("No"), this) {}
};
struct TimeHuntButton : public BoolOptionButton {
TimeHuntButton() : BoolOptionButton("TimeHunting", N_("Yes"), N_("No"), this) {}
};
struct RatingsUpdateButton : public BoolOptionButton {
RatingsUpdateButton() : BoolOptionButton("RatingsAutoUpdate", N_("Auto"), N_("Never"), this) {}
};
class VideoModeButton : public TextButton {
video::VideoModes get_mode() const {
int mode = Clamp(options::GetInt("VideoMode"), 0, int(video::VM_COUNT));
return static_cast<video::VideoModes>(mode);
}
string get_text() const {
return GetInfo(get_mode())->name;
}
void on_action(Widget *) {
int mode = get_mode();
// cycle at most once through all available video modes
do {
mode += 1;
if (mode >= video::VM_COUNT)
mode = 0;
const video::VMInfo *vminfo = GetInfo (static_cast<video::VideoModes>(mode));
if (vminfo->available) {
options::SetOption("VideoMode", mode);
invalidate();
break;
}
} while (mode != get_mode());
}
public:
VideoModeButton() : TextButton(this) { }
};
/* -------------------- SoundSetButton -------------------- */
SoundSetButton::SoundSetButton() : ValueButton(0, 1) {
int numAvail = sound::GetOptionSoundSetCount();
setMaxValue(numAvail - 1);
init();
}
int SoundSetButton::get_value() const {
return sound::GetOptionSoundSet();
}
void SoundSetButton::set_value(int value) {
sound::SetOptionSoundSet(value);
}
string SoundSetButton::get_text(int value) const {
return _(sound::GetOptionSoundSetText(value).c_str());
}
/* -------------------- StereoButton -------------------- */
StereoButton::StereoButton() : ValueButton(-1, 1)
{
init();
}
int StereoButton::get_value() const
{
double separation = options::GetDouble("StereoSeparation");
if (separation == 0)
return 0;
else
return (separation > 0) ? 1 : -1;
}
void StereoButton::set_value(int value)
{
if (value == 0)
options::SetOption("StereoSeparation", 0.0);
else if (value > 0)
options::SetOption("StereoSeparation", 10.0);
else
options::SetOption("StereoSeparation", -10.0);
}
string StereoButton::get_text(int value) const
{
switch (value) {
case -1: return _("reversed");
case 0: return _("mono");
case 1: return _("normal");
}
assert(0);
return string();
}
/* -------------------- FullscreenButton -------------------- */
FullscreenButton::FullscreenButton()
: BoolOptionButton("FullScreen", N_("Yes"), N_("No"), this)
{
}
/* -------------------- LanguageButton -------------------- */
struct Language {
const char *name;
const char *localename;
};
Language languages[] = {
{ "default", "" },
{ "Deutsch", "de_DE" },
{ "English", "en_EN" },
{ "Español", "es_ES" },
{ "Français", "fr_FR" },
{ "Italiano", "it_IT" },
{ "Nederlands", "nl_NL" },
{ "Svenska", "sv_SE" },
{ "Русский", "ru_RU" },
{ "Magyar", "hu_HU" },
{ "Português", "pt_BR" },
{ "Suomi", "fi_FI" },
};
int LanguageButton::get_value() const
{
string localename; // = ecl::DefaultMessageLocale ();
options::GetOption ("Language", localename);
int lang = 0; // unknown language
for (size_t i=0; i<NUMENTRIES(languages); ++i) {
if (localename == languages[i].localename)
lang = int(i);
}
return lang;
}
void LanguageButton::set_value(int value)
{
options::SetOption ("Language", languages[value].localename);
if ( not inInit) {
// change language only on user action
app.setLanguage(languages[value].localename);
myListener->on_action(this);
}
}
string LanguageButton::get_text(int value) const
{
if (value == -1)
return _("unknown");
else
return languages[value].name;
}
LanguageButton::LanguageButton (ActionListener *al)
: ValueButton(0, NUMENTRIES(languages)-1), myListener(al)
{
inInit = true;
init();
inInit = false;
}
/* -------------------- GammaButton -------------------- */
GammaButton::GammaButton()
: ValueButton(1, 10)
{
init();
}
void GammaButton::set_value(int value)
{
double gamma = double(value) / 5.0;
options::SetOption ("Gamma", gamma);
video::UpdateGamma();
}
int GammaButton::get_value() const
{
double gamma = options::GetDouble ("Gamma");
int value = round_down<int>(gamma * 5.0);
return value;
}
string GammaButton::get_text(int value) const
{
return ecl::strf ("%d", value-5);
}
/* -------------------- Options Menu -------------------- */
OptionsMenu::OptionsMenu(ecl::Surface *background_)
: back(new StaticTextButton(N_("Back"), this)),
fullscreen(new FullscreenButton),
m_restartinfo (new Label("")),
background(background_),
previous_caption(video::GetCaption())
{
const int spacing = 5;
const int big_spacing = 60;
const int label_width = 180;
const int but_width = 100;
const int but_height = 30;
const video::VMInfo *vminfo = video::GetInfo();
int hmargin = vminfo->width < 660 ? 10 : (vminfo->width < 900 ? 20 : 80);
int midspacing = vminfo->width - 2*hmargin - 2*but_width - 2*label_width;
BuildVList leftlabels (this, Rect(-label_width, 0, label_width, but_height), spacing);
BuildVList left (this, Rect(0, 0, but_width, but_height), spacing);
BuildVList rightlabels (this, Rect(but_width+midspacing, 0, label_width, but_height), spacing);
BuildVList right(this, Rect(but_width+midspacing+label_width, 0, but_width, but_height), spacing);
leftlabels.add (new Label(N_("Language: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Fullscreen: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Video mode: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Gamma correction: "), HALIGN_RIGHT));
leftlabels.add (new Label(N_("Mouse speed: "), HALIGN_RIGHT));
language = new LanguageButton(this);
left.add (language);
left.add (fullscreen);
left.add (new VideoModeButton);
left.add (new GammaButton);
left.add (new MouseSpeedButton);
rightlabels.add (new Label(N_("Sound volume: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Sound set: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Music volume: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Stereo: "), HALIGN_RIGHT));
rightlabels.add (new Label(N_("Ratings update: "), HALIGN_RIGHT));
right.add (new SoundVolumeButton);
right.add (new SoundSetButton);
right.add (new MusicVolumeButton);
// right.add (new InGameMusicButton);Über
right.add (new StereoButton);
right.add (new RatingsUpdateButton);
Rect l = left.pos();
Rect r = right.pos();
BuildVList bottomlabels (this, Rect(-label_width, Max(l.y, r.y), label_width, but_height), spacing);
BuildVList bottom (this, Rect(0, Max(l.y, r.y), vminfo->width - 2*hmargin - label_width, but_height), spacing);
bottomlabels.add (new Label(N_("User name: "), HALIGN_RIGHT));
bottomlabels.add (new Label(N_("User path: "), HALIGN_RIGHT));
bottomlabels.add (new Label(N_("User image path: "), HALIGN_RIGHT));
userNameTF = new TextField(app.state->getString("UserName"));
userNameTF->setMaxChars(20);
userNameTF->setInvalidChars("+");
bottom.add (userNameTF);
userPathTF = new TextField(XMLtoUtf8(LocalToXML(app.userPath.c_str()).x_str()).c_str());
bottom.add (userPathTF);
userImagePathTF = new TextField(XMLtoUtf8(LocalToXML(app.userImagePath.c_str()).x_str()).c_str());
bottom.add (userImagePathTF);
// add (m_restartinfo, Rect (l.x, l.y + 15, 400, 20));
// m_restartinfo->set_alignment (HALIGN_LEFT);
// update_info();
Rect b = bottom.pos();
l.x = (l.x+r.x)/2;
l.y = b.y+big_spacing;
add(back, l);
}
OptionsMenu::~OptionsMenu() {
video::SetCaption(previous_caption.c_str());
}
// void OptionsMenu::update_info()
// {
// if (options::MustRestart)
// m_restartinfo->set_text (
// N_("Please restart Enigma to activate your changes!"));
// else
// m_restartinfo->set_text ("");
// }
void OptionsMenu::quit() {
std::string tfUserPathLocal = XMLtoLocal(Utf8ToXML(userPathTF->getText().c_str()).x_str()).c_str();
std::string tfUserImageLocal = XMLtoLocal(Utf8ToXML(userImagePathTF->getText().c_str()).x_str()).c_str();
if ((app.state->getString("UserName") != userNameTF->getText())
|| (app.userPath != tfUserPathLocal ) || (app.userImagePath != tfUserImageLocal)) {
// ensure that enigma.score is saved with new Username or to new location
lev::ScoreManager::instance()->markModified();
}
// strip off leading and trailing whitespace from user name
std::string userName = userNameTF->getText();
std::string::size_type firstChar = userName.find_first_not_of(" ");
std::string::size_type lastChar = userName.find_last_not_of(" ");
if (firstChar != std::string::npos)
app.state->setProperty("UserName", userName.substr(firstChar, lastChar - firstChar + 1));
else
app.state->setProperty("UserName", std::string(""));
app.setUserPath(tfUserPathLocal.c_str());
app.setUserImagePath(tfUserImageLocal.c_str());
Menu::quit();
}
bool OptionsMenu::on_event (const SDL_Event &e)
{
bool handled=false;
if (e.type == SDL_MOUSEBUTTONDOWN
&& e.button.button == SDL_BUTTON_RIGHT)
{
quit();
handled = true;
}
else if (e.type == SDL_KEYUP) {
if ((e.key.keysym.sym==SDLK_RETURN) &&
(e.key.keysym.mod & KMOD_ALT))
{
// update state of FullscreenButton :
fullscreen->invalidate();
handled = true;
}
}
return handled;
}
void OptionsMenu::on_action(Widget *w)
{
if (w == back)
quit();
else if (w == language)
// language changed - retranslate and redraw everything
invalidate_all();
}
void OptionsMenu::tick (double)
{
// update_info();
}
void OptionsMenu::draw_background(ecl::GC &gc)
{
video::SetCaption(("Enigma - Options Menu"));
// blit(gc, 0,0, enigma::GetImage("menu_bg"));
blit(gc, 0,0, background);
}
/* -------------------- Functions -------------------- */
void ShowOptionsMenu(Surface *background) {
if (background == 0)
background = enigma::GetImage("menu_bg", ".jpg");
OptionsMenu m(background);
m.center();
m.manage();
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck
*
* 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 GUI_OPTIONSMENU_HH_INCLUDED
#define GUI_OPTIONSMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
namespace enigma { namespace gui {
/* -------------------- OptionsMenu -------------------- */
class OptionsMenu : public Menu {
public:
OptionsMenu(ecl::Surface *background_);
~OptionsMenu();
virtual void quit();
private:
void update_info();
// ActionListener interface.
bool on_event (const SDL_Event &e);
void on_action(gui::Widget *w);
// Menu interface.
void draw_background(ecl::GC &gc);
void tick(double dtime);
// Variables.
gui::Widget *back, *fullscreen, *language;
gui::TextField *userNameTF;
gui::TextField *userPathTF;
gui::TextField *userImagePathTF;
gui::Label *m_restartinfo;
ecl::Surface *background;
std::string previous_caption;
};
/* -------------------- Options Buttons -------------------- */
struct FullscreenButton : public BoolOptionButton {
FullscreenButton();
};
class StereoButton : public ValueButton {
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
public:
StereoButton();
};
class SoundSetButton : public ValueButton {
public:
SoundSetButton();
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
};
class LanguageButton : public ValueButton {
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
bool inInit;
ActionListener *myListener;
public:
// second user action listener: first one is misused by ValueButton
LanguageButton (ActionListener *al = 0);
};
class GammaButton : public ValueButton {
int get_value() const;
void set_value(int value);
std::string get_text(int value) const;
public:
GammaButton ();
};
/* -------------------- Functions -------------------- */
void ShowOptionsMenu(ecl::Surface *background);
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,109 @@
/*
* 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 "gui/ScreenshotViewer.hh"
#include "gui/HelpMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "main.hh"
#include "nls.hh"
#include "video.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
static const char *helptext[] = {
N_("Escape:"), N_("Back"),
"F1:", N_("Show this help"),
N_("Page Up:"), N_("Show previous screenshot"),
N_("Page Down:"), N_("Show next screenshot"),
0
};
ScreenshotViewer::ScreenshotViewer(lev::Proxy *aLevel) :
levelProxy (aLevel), shotNumber (0) {
}
ScreenshotViewer::~ScreenshotViewer() {
}
bool ScreenshotViewer::on_event (const SDL_Event &e) {
switch (e.type) {
case SDL_MOUSEBUTTONDOWN:
if (e.button.button == SDL_BUTTON_RIGHT) {
Menu::quit();
return true;
}
break;
case SDL_KEYDOWN:
SDLKey keysym = e.key.keysym.sym;
switch (keysym) {
case SDLK_PAGEUP:
if (shotNumber > 0 )
--shotNumber;
invalidate_all();
return true;
case SDLK_PAGEDOWN:
shotNumber++;
invalidate_all();
return true;
// eat up widget activation keys - we have no widgets
case SDLK_DOWN:
case SDLK_UP:
case SDLK_RIGHT:
case SDLK_LEFT:
return true;
case SDLK_F1:
displayHelp(helptext, 200);
invalidate_all();
return true;
default:
break;
}
break;
}
return false;
}
void ScreenshotViewer::draw_background (ecl::GC &gc) {
std::string filename = "screenshots/" +
levelProxy->getLocalSubstitutionLevelPath() +
(shotNumber > 0 ? ecl::strf("#%d", shotNumber) : "") + ".png";
std::string fullPath;
if (app.resourceFS->findFile(filename, fullPath)) {
const video::VMInfo *vminfo = video::GetInfo();
ecl::Surface * image = ecl::LoadImage(fullPath.c_str());
if (image->width() == vminfo->width && image->height() == vminfo->height) {
blit(gc, 0,0, image);
} else {
ecl::Surface * imageZoomed = image->zoom(vminfo->width, vminfo->height);
blit(gc, 0,0, imageZoomed);
delete imageZoomed;
}
delete image;
} else {
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
Font *f = enigma::GetFont("menufont");
f->render (gc, 30, 60, _("No screenshot available:"));
f->render (gc, 30, 100, filename.c_str());
}
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,49 @@
/*
* 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 GUI_SCREENSHOTVIEWER_HH_INCLUDED
#define GUI_SCREENSHOTVIEWER_HH_INCLUDED
#include "gui/Menu.hh"
#include "lev/Proxy.hh"
namespace enigma { namespace gui {
/**
* TODO delete CTRL-D with confirmation panel - rename of following shots?
* TODO shift up, down for reorder/rename of shots
*/
class ScreenshotViewer : public gui::Menu {
public:
ScreenshotViewer (lev::Proxy *aLevel);
~ScreenshotViewer ();
// Widget interface
virtual bool on_event (const SDL_Event &e);
protected:
// Menu interface.
virtual void draw_background (ecl::GC &gc);
private:
lev::Proxy *levelProxy;
std::string basePath;
int shotNumber;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,91 @@
/*
* 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 "gui/SearchMenu.hh"
#include "ecl.hh"
#include "enigma.hh"
#include "errors.hh"
#include "lev/Index.hh"
#include "lev/Proxy.hh"
#include "nls.hh"
#include "video.hh"
#include "main.hh"
using namespace ecl;
using namespace std;
namespace enigma { namespace gui {
SearchMenu::SearchMenu() : didSearch (false) {
const video::VMInfo &vminfo = *video::GetInfo();
ecl::Font *menufont = enigma::GetFont("menufont");
Label * shallowTitle = new Label(N_("Shallow Search:"), HALIGN_LEFT);
// TRANSLATORS: the translation can have double size of the english text
std::string helpText = _("case independent search in title, author, filename");
std::string workString = helpText;
std::string::size_type breakPos = breakString(menufont, workString,
" ", 380);
Label * shallowHelp1 = new UntranslatedLabel(workString.substr(0,breakPos), HALIGN_LEFT);
Label * shallowHelp2 = new UntranslatedLabel(workString.substr(breakPos), HALIGN_LEFT);
shallowSearch = new TextField("", this);
this->add(shallowTitle, Rect(vminfo.width/2 - 190, vminfo.height/2 - 100, 380, 35));
this->add(shallowHelp1, Rect(vminfo.width/2 - 190, vminfo.height/2 - 40, 380, 25));
this->add(shallowHelp2, Rect(vminfo.width/2 - 190, vminfo.height/2 - 10, 380, 25));
this->add(shallowSearch, Rect(vminfo.width/2 - 190, vminfo.height/2 + 55, 380, 35));
// Create buttons - positioning identical to Levelmenu
Label * dummy1 = new Label();
Label * dummy2 = new Label();
but_ignore = new StaticTextButton(N_("Undo"), this);
but_search = new StaticTextButton(N_("Search"), this);
HList * commandHList = new HList;
commandHList->set_spacing(10);
commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
commandHList->set_default_size(140, 35);
commandHList->add_back(dummy1);
commandHList->add_back(dummy2);
commandHList->add_back(but_ignore);
commandHList->add_back(but_search);
this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35));
}
bool SearchMenu::isSearchQuit() {
return didSearch;
}
void SearchMenu::on_action(Widget *w) {
if (w == but_search ||
(w == shallowSearch && shallowSearch->wasLastActionReturn())) {
lev::Index::setCurrentIndex(lev::Proxy::search(shallowSearch->getText()));
didSearch = true;
Menu::quit();
} else if (w == but_ignore) {
Menu::quit();
}
}
void SearchMenu::draw_background(ecl::GC &gc) {
video::SetCaption(("Enigma - Search Menu"));
blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg"));
}
}} // namespace enigma::gui

View File

@@ -0,0 +1,43 @@
/*
* 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 GUI_SEARCHMENU_HH_INCLUDED
#define GUI_SEARCHMENU_HH_INCLUDED
#include "gui/Menu.hh"
#include "gui/TextField.hh"
namespace enigma { namespace gui {
class SearchMenu : public gui::Menu {
public:
SearchMenu ();
void on_action(Widget *w);
void draw_background(ecl::GC &gc);
bool isSearchQuit();
private:
TextField *shallowSearch;
Widget *but_ignore;
Widget *but_search;
bool didSearch;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2005 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 "main.hh"
#include "gui/widgets.hh"
#include "gui/TextField.hh"
#include "ecl_utf.hh"
#include "enigma.hh"
#include "sound.hh"
#include "video.hh"
#include "options.hh"
#include "nls.hh"
#include "ecl.hh"
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace enigma::gui;
using namespace ecl;
using namespace std;
#define SCREEN ecl::Screen::get_instance()
ecl::Font *TextField::menufont = 0;
TextField::TextField(const std::string &t, ActionListener *al) : cursorTime(0),
showCursor(true), isLastActionReturn (false), invalidChars(""),
isLimitedToValidChars(false), maxChars(-1) {
if (menufont == 0) {
menufont = enigma::GetFont("menufont");
}
set_listener(al);
textPreCursor = t;
ecl::utf8CharSizes(textPreCursor, charSizesPreCursor);
textPostCursor= "";
}
void TextField::set_text(const std::string &t) {
textPreCursor = t;
charSizesPreCursor.clear();
ecl::utf8CharSizes(textPreCursor, charSizesPreCursor);
textPostCursor= "";
charSizesPostCursor.clear();
}
std::string TextField::getText() {
string total = textPreCursor;
total += textPostCursor;
return total;
}
void TextField::setInvalidChars(std::string forbiddenChars) {
invalidChars = forbiddenChars;
}
void TextField::setMaxChars(int max) {
maxChars = max;
}
bool TextField::wasLastActionReturn() {
return isLastActionReturn;
}
void TextField::tick (double dtime) {
cursorTime += dtime;
if (cursorTime > 0.5) {
cursorTime = 0.0;
showCursor = !showCursor;
invalidate();
}
}
void TextField::draw(ecl::GC &gc, const ecl::Rect &r) {
Button::draw(gc,r);
Font *f = menufont;
int h = f->get_height();
int w_pre = f->get_width(textPreCursor.c_str());
int w_post = f->get_width(textPostCursor.c_str());
int w_cursor = m_activep ? 1 : 0;
int x = get_x() + (get_w()- w_pre - w_post - w_cursor )/2;
int y = get_y() + (get_h()-h)/2;
// cursor always visible
if (x + w_pre < get_x() + 5)
// cursor would be left of textfield - shift centered text right
x = get_x() + 5 - w_pre;
if (x + w_pre > get_x() + get_w() - 5)
// cursor would be right of textfiled - shift centered text left
x = get_x() + get_w() - 5 - w_pre;
f->render (gc, x, y, textPreCursor.c_str());
x += w_pre;
if (m_activep) {
if (showCursor) {
set_color(gc, 200,200,200);
vline(gc, x, y, h);
}
x += w_cursor;
}
f->render (gc, x, y, textPostCursor.c_str());
}
bool TextField::on_event(const SDL_Event &e) {
bool handeled = false;
bool modified = false;
switch (e.type) {
case SDL_MOUSEBUTTONDOWN:
// set cursor
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym) {
case SDLK_RETURN:
handeled = true;
isLastActionReturn = true;
invoke_listener();
break;
case SDLK_RIGHT:
if(textPostCursor.size() > 0) {
int size = charSizesPostCursor.back();
charSizesPostCursor.pop_back();
charSizesPreCursor.push_back(size);
textPreCursor.append(textPostCursor, 0, size);
textPostCursor.erase(0, size);
}
invalidate();
handeled = true;
break;
case SDLK_LEFT:
if(textPreCursor.size() > 0) {
int size = charSizesPreCursor.back();
charSizesPreCursor.pop_back();
charSizesPostCursor.push_back(size);
textPostCursor.insert(0, textPreCursor.substr(textPreCursor.size() - size));
textPreCursor.erase(textPreCursor.size() - size);
}
invalidate();
handeled = true;
break;
case SDLK_INSERT:
handeled = true;
break;
case SDLK_HOME:
if(textPreCursor.size() > 0) {
int i;
int preChars = charSizesPreCursor.size();
for (i = 0; i < preChars; i++) {
int size = charSizesPreCursor.back();
charSizesPreCursor.pop_back();
charSizesPostCursor.push_back(size);
}
textPostCursor.insert(0, textPreCursor);
textPreCursor.clear();
}
invalidate();
handeled = true;
break;
case SDLK_END:
if(textPostCursor.size() > 0) {
int size;
int i;
int postChars = charSizesPostCursor.size();
for (i = 0; i < postChars; i++) {
size = charSizesPostCursor.back();
charSizesPostCursor.pop_back();
charSizesPreCursor.push_back(size);
}
textPreCursor.append(textPostCursor);
textPostCursor.clear();
}
invalidate();
handeled = true;
break;
case SDLK_DELETE:
if(textPostCursor.size() > 0) {
int size = charSizesPostCursor.back();
textPostCursor.erase(0, size);
charSizesPostCursor.pop_back();
}
invalidate();
handeled = true;
modified = true;
break;
case SDLK_BACKSPACE:
if(textPreCursor.size() > 0) {
int size = charSizesPreCursor.back();
textPreCursor.erase(textPreCursor.size() - size);
charSizesPreCursor.pop_back();
}
invalidate();
handeled = true;
modified = true;
break;
case SDLK_ESCAPE:
case SDLK_DOWN:
case SDLK_UP:
// menu active widget movements
break;
default:
// get char
if (e.key.keysym.unicode != 0 ) {
UTF16 realChar;
if (e.key.keysym.unicode >= 0x20 &&
(e.key.keysym.unicode < 0x80 || // key pad
e.key.keysym.sym < 0x80)) { // windows umlaute
// the unicode is correct in these cases
realChar = e.key.keysym.unicode;
}
else if (e.key.keysym.unicode >= 0x80 &&
e.key.keysym.sym < 0x100) {
// Linux: bad unicode but sym is ISO-8859-1
// incomplete workaround - runs only for some lower
// case umlauts
// we would need to handle shift key in language
// dependent manner -- or fix SDL Linux
realChar = e.key.keysym.sym;
}
else {
// chars like ctrl-a - ctrl-z
sound::EmitSoundEvent ("menustop");
break;
}
if ((maxChars >= 0 && (charSizesPreCursor.size() + charSizesPostCursor.size()) >= maxChars) ||
realChar < 0x100 && invalidChars.find((char)realChar) != std::string::npos) {
// string too long or invalid char
sound::EmitSoundEvent ("menustop");
break;
}
unsigned char utf8Char[4];
UTF16 const * utf16Ptr = (UTF16 *)&realChar;
UTF8 * utf8Ptr = utf8Char;
ConversionResult result;
result = ConvertUTF16toUTF8 (&utf16Ptr, utf16Ptr + 1,
&utf8Ptr, utf8Char + 4, strictConversion);
*utf8Ptr = 0;
textPreCursor += (const char *)utf8Char;
charSizesPreCursor.push_back(utf8Ptr - utf8Char);
invalidate();
handeled = true;
modified = true;
break;
}
if (e.key.keysym.sym < 300 || e.key.keysym.sym > 314 ){
// chars like PageUp, F1 but not key state modifier
// like shift, alt,...
sound::EmitSoundEvent ("menustop");
}
break;
}
break;
default:
break;
}
if (modified) {
isLastActionReturn = false;
invoke_listener();
}
return handeled;
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2005 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 GUI_TF_HH_INCLUDED
#define GUI_TF_HH_INCLUDED
#include "gui/widgets.hh"
namespace enigma { namespace gui {
/**
* Gui widget for simple text insertion. Cursor keys, backspace and delete,
* home and end are supported. As a widget it handels utf-8 strings. But
* in contrast to other widget there is of course no gettext l10n translation.
* This widget is designed to be part of a menu.
* <par>
* ToDo: supply full Linux umlaut support;
* add hook for external or subclass char insertion validation
* (NumberField, ...);
* move mouse cursor out of Textfield without deselection
*/
class TextField : public Button {
public:
/**
* Creates a boarderd text input field.
* @param t preset utf-8 input string
* @param al currently unused
*/
TextField(const std::string &t = "", ActionListener *al=0);
/**
* resets the input string.
* @param t new utf-8 input string
*/
void set_text(const std::string &t);
/**
* returns the inserted text
* @return the utf-8 coded string
*/
std::string getText();
void setInvalidChars(std::string forbiddenChars); // currently limited to 1st coding page
void setMaxChars(int max);
bool wasLastActionReturn();
// Widget interface.
virtual void tick (double dtime);
virtual bool on_event(const SDL_Event &/*e*/);
void draw(ecl::GC &gc, const ecl::Rect &r);
protected:
double cursorTime;
bool showCursor;
private:
std::string textPreCursor;
std::string textPostCursor;
/**
* a byte vector describing for each utf-8 character the number of
* occupied bytes in the string.
*/
std::vector<unsigned char> charSizesPreCursor;
/**
* a byte vector describing for each utf-8 character the number of
* occupied bytes in the string. This vector is in reverse order to
* the string itself!!
*/
std::vector<unsigned char> charSizesPostCursor;
bool isLimitedToValidChars;
std::string validChars;
std::string invalidChars;
int maxChars;
bool isLastActionReturn;
static ecl::Font *menufont;
};
}} // namespace enigma::gui
#endif

View File

@@ -0,0 +1,943 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 "gui/widgets.hh"
#include "enigma.hh"
#include "sound.hh"
#include "video.hh"
#include "options.hh"
#include "nls.hh"
#include "ecl.hh"
#include "ecl_font.hh"
#include <cassert>
#include <algorithm>
#include <iostream>
#include <climits>
using namespace enigma::gui;
using namespace ecl;
using namespace std;
#define SCREEN ecl::Screen::get_instance()
/* -------------------- Widget -------------------- */
Widget::Widget(Container *parent)
: area(), m_parent(parent), m_listener(0)
{}
void Widget::invalidate() {
if (m_parent)
m_parent->invalidate_area(get_area());
}
void Widget::invalidate_area(const ecl::Rect& r) {
if (m_parent)
m_parent->invalidate_area(r);
}
void Widget::reconfigure()
{
if (m_parent)
m_parent->reconfigure_child(this);
}
void Widget::invoke_listener() {
if (m_listener)
m_listener->on_action(this);
}
void Widget::move(int x, int y) {
area.x = x; area.y = y;
}
void Widget::resize (int w, int h) {
area.w = w; area.h = h;
}
bool Widget::on_event(const SDL_Event &e) {
switch (e.type) {
case SDL_KEYDOWN:
modifierKeys = e.key.keysym.mod;
mouseButton = SDL_BUTTON_LEFT;
break;
case SDL_MOUSEBUTTONDOWN:
modifierKeys = SDL_GetModState();
mouseButton = e.button.button;
break;
}
return false;
}
/* -------------------- Image -------------------- */
void Image::draw (ecl::GC &gc, const ecl::Rect &/*r*/) {
// if (ecl::Surface *s = enigma::GetImage(imgname.c_str()))
// blit(gc, get_x(), get_y(), s);
if (ecl::Surface *s = enigma::GetImage(imgname.c_str())) {
int w=s->width();
int h=s->height();
int x = get_x() + (get_w()-w)/2;
int y = get_y() + (get_h()-h)/2;
blit(gc, x, y, s);
}
}
/* -------------------- AreaManager -------------------- */
AreaManager::AreaManager(Container *c)
: top_container(c)
{
assert(top_container->get_parent() == 0); // otherwise it's not the top_container
}
void AreaManager::invalidate_area(const ecl::Rect &r) {
dirtyrects.add(r);
}
void AreaManager::invalidate_all() {
dirtyrects.clear();
dirtyrects.push_back(SCREEN->size());
}
void AreaManager::refresh() {
if (!dirtyrects.empty()) {
video::HideMouse();
GC gc(SCREEN->get_surface());
for (RectList::iterator i = dirtyrects.begin(); i!=dirtyrects.end(); ++i) {
top_container->draw(gc, *i);
SCREEN->update_rect(*i);
}
video::ShowMouse();
dirtyrects.clear();
}
}
/* -------------------- Container -------------------- */
Container::Container()
: managed_by(0)
{
}
Container::~Container() {
clear();
delete managed_by;
}
AreaManager *Container::getAreaManager() {
if (Container *p = get_parent()) {
assert(!managed_by);
return p->getAreaManager();
}
if (!managed_by) {
managed_by = new AreaManager(this);
}
return managed_by;
}
void Container::invalidate_area(const ecl::Rect &r) { getAreaManager()->invalidate_area(r); }
void Container::invalidate_all() { getAreaManager()->invalidate_all(); }
void Container::refresh() { getAreaManager()->refresh(); }
void Container::clear() {
delete_sequence(m_widgets.begin(), m_widgets.end());
m_widgets.clear();
}
void Container::add_child (Widget *w) {
if (w && w != this) {
m_widgets.push_back(w);
w->set_parent(this);
// w->move (get_x() + w->get_x(),
// get_y() + w->get_y());
}
}
void Container::remove_child (Widget *w) {
for (iterator it = begin(); it != end(); it++) {
if (*it == w) {
m_widgets.erase(it);
return;
}
}
}
void Container::exchange_child (Widget *oldChild, Widget *newChild) {
for (int i = 0; i < m_widgets.size(); i++) {
if (m_widgets[i] == oldChild) {
m_widgets[i] = newChild;
newChild->set_parent(this);
return;
}
}
}
void Container::draw (ecl::GC& gc, const ecl::Rect &r) {
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
Rect rr = intersect(r, w->get_area());
clip(gc, rr);
w->draw(gc,rr);
}
}
void Container::draw_all() {
invalidate_all();
refresh();
}
void Container::reconfigure_child (Widget *)
{
}
Widget * Container::find_widget(int x, int y) {
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
if (w->get_area().contains(x,y)) {
Container *c = dynamic_cast<Container *> (w);
if (c) {
w = c->find_widget (x, y);
return w ? w : c;
}
return w;
}
}
return 0;
}
Widget * Container::find_adjacent_widget(Widget *from, int x, int y) {
// valid values for x/y : 1/0, -1/0, 0/1, 0/-1
assert(from && x>=-1 && x<=1 && y>=-1 && y<=1 && abs(x+y) == 1);
if (!from) return 0;
int best_distance = INT_MAX;
Widget *best_widget = 0;
ecl::Rect farea = from->get_area();
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
ecl::Rect warea = w->get_area();
bool adjacent = true;
int distance = 0;
if (x) { // check for y-overlap
if (farea.y>(warea.y+warea.h-1) || warea.y>(farea.y+farea.h-1)) {
adjacent = false;
}
else {
distance = (warea.x-farea.x)*x;
}
}
else { // check for x-overlap
if (farea.x>(warea.x+warea.h-1) || warea.x>(farea.x+farea.h-1)) {
adjacent = false;
}
else {
distance = (warea.y-farea.y)*y;
}
}
if (adjacent && distance>0 && distance<best_distance) {
best_distance = distance;
best_widget = w;
}
}
return best_widget;
}
void Container::move (int x, int y) {
Rect a = get_area();
a.x = x;
a.y = y;
this->set_area (a);
int dx = x-get_x();
int dy = y-get_y();
for (iterator i=begin(); i!=end(); ++i) {
Widget *w = *i;
w->move(dx + w->get_x(), dy+w->get_y());
}
}
ecl::Rect Container::boundingbox() {
if (!m_widgets.empty()) {
iterator i=begin();
Rect bbox=(*i)->get_area();
for (++i; i!=end(); ++i)
bbox = ecl::boundingbox(bbox, (*i)->get_area());
return bbox;
} else
return get_area();
}
/* -------------------- List -------------------- */
List::List (int spacing)
: m_spacing(spacing),
has_default_size (false),
defaultw (0),
defaulth (0),
m_halign (HALIGN_LEFT),
m_valign (VALIGN_TOP)
{}
void List::remove_child (Widget *w) {
Container::remove_child(w);
recalc();
}
void List::exchange_child(Widget *oldChild, Widget *newChild) {
Container::exchange_child(oldChild, newChild);
recalc();
}
void List::set_spacing (int pixels)
{
m_spacing = pixels;
}
int List::get_spacing () const
{
return m_spacing;
}
int List::calc_minimum_height() const
{
int sum=0;
const WidgetList &wl = m_widgets;
if (!wl.empty()) {
sum = (wl.size() - 1) * m_spacing;
for (WidgetList::const_iterator i=wl.begin(); i!=wl.end(); ++i) {
int nw, nh;
get_size (*i, nw, nh);
sum += nh;
}
}
return sum;
}
int List::calc_minimum_width () const
{
int sum=0;
const WidgetList &wl = m_widgets;
if (!wl.empty()) {
sum = (wl.size() - 1) * m_spacing;
for (WidgetList::const_iterator i=wl.begin(); i!=wl.end(); ++i) {
int nw, nh;
get_size (*i, nw, nh);
sum += nw;
}
}
return sum;
}
void List::set_default_size (int w, int h)
{
has_default_size = true;
defaultw = w;
defaulth = h;
}
void List::get_size (const Widget *widget, int &w, int &h) const
{
if (has_default_size)
w = defaultw, h = defaulth;
else
widget->naturalsize (w, h);
}
void List::resize (int w, int h)
{
Container::resize (w, h);
recalc();
}
void List::move (int x, int y)
{
Container::move (x, y);
// recalc();
}
void List::reconfigure_child (Widget *w)
{
Container::reconfigure_child (w);
recalc();
invalidate();
}
void List::add_back (Widget *w, ExpansionMode m)
{
add_child (w);
m_expansionmodes.push_back (m);
recalc();
}
void List::set_alignment (HAlignment halign, VAlignment valign)
{
if (halign != m_halign || valign != m_valign) {
m_halign = halign;
m_valign = valign;
recalc();
}
}
/* -------------------- HList -------------------- */
void HList::recalc()
{
int targetw = this->get_w(); // The available space
int naturalw= calc_minimum_width();
int excessw = targetw - naturalw;
int num_expand = std::count (m_expansionmodes.begin(),
m_expansionmodes.end(),
List::EXPAND);
WidgetList::iterator i = m_widgets.begin(),
end = m_widgets.end();
int x = get_x(), y = get_y();
size_t j = 0;
if (num_expand == 0 && excessw > 0) {
switch (m_halign) {
case HALIGN_CENTER:
x += excessw / 2;
excessw = 0;
break;
default:
break;
}
}
for (; i != end; ++i, ++j) {
int w, h;
List::get_size (*i, w, h);
if (excessw > 0 && m_expansionmodes[j] == List::EXPAND) {
w += excessw / num_expand;
excessw -= excessw / num_expand;
num_expand -= 1;
}
(*i)->move (x, y);
(*i)->resize (w, get_h());
x += w + get_spacing();
}
}
bool HList::fits() {
int targetw = this->get_w(); // The available space
int naturalw= calc_minimum_width();
return targetw >= naturalw;
}
/* -------------------- VList -------------------- */
void VList::recalc()
{
int targeth = this->get_h(); // The available space
int naturalh= calc_minimum_height();
int excessh = targeth - naturalh;
int num_expand = std::count (m_expansionmodes.begin(),
m_expansionmodes.end(),
List::EXPAND);
WidgetList::iterator i = m_widgets.begin(),
end = m_widgets.end();
int x = get_x(), y = get_y();
size_t j = 0;
if (num_expand == 0 && excessh > 0) {
switch (m_valign) {
case VALIGN_CENTER:
y += excessh / 2;
excessh = 0;
break;
default:
break;
}
}
for (; i != end; ++i, ++j) {
int w, h;
List::get_size (*i, w, h);
if (excessh > 0 && m_expansionmodes[j] == List::EXPAND) {
h += excessh / num_expand;
excessh -= excessh / num_expand;
num_expand -= 1;
}
(*i)->move (x, y);
(*i)->resize (get_w(), h);
y += h + get_spacing();
}
}
bool VList::fits() {
int targeth = this->get_h(); // The available space
int naturalh= calc_minimum_height();
return targeth >= naturalh;
}
/* -------------------- Label -------------------- */
Label::Label (const std::string &text,
HAlignment halign,
VAlignment valign)
: m_text (text),
m_font(enigma::GetFont("menufont")),
m_halign(halign),
m_valign(valign)
{}
void Label::set_text (const std::string &text) {
if (text != m_text) {
m_text = text;
reconfigure();
invalidate();
}
}
string Label::get_text() const {
return _(m_text.c_str());
}
string Label::getText() const {
return m_text;
}
void Label::set_font (ecl::Font *font) {
if (m_font != font) {
m_font = font;
reconfigure();
invalidate();
}
}
bool Label::text_fits(double area_fraction) {
int w, h;
naturalsize (w, h);
return w <= get_w()*area_fraction;
}
void Label::draw (ecl::GC &gc, const ecl::Rect &)
{
Font *f = m_font;
int w, h;
naturalsize (w, h);
int x = get_x(), y=get_y();
switch (m_halign) {
case HALIGN_LEFT: break;
case HALIGN_RIGHT: x += get_w() - w; break;
case HALIGN_CENTER: x += (get_w()-w)/2; break;
}
switch (m_valign) {
case VALIGN_TOP: break;
case VALIGN_BOTTOM: y += get_h() - h; break;
case VALIGN_CENTER: y += (get_h()-h)/2; break;
}
// translate if not an empty string
f->render (gc, x, y, m_text == "" ? "" : get_text().c_str());
}
void Label::set_alignment (HAlignment halign, VAlignment valign) {
if (halign != m_halign || valign != m_valign) {
m_halign = halign;
m_valign = valign;
invalidate();
}
}
void Label::naturalsize (int &w, int &h) const
{
h = m_font->get_height();
// width of translation if not an empty string
w = m_font->get_width (m_text == "" ? "" : get_text().c_str());
}
/* -------------------- UntranslatedLabel -------------------- */
UntranslatedLabel::UntranslatedLabel (const std::string &text,
HAlignment halign, VAlignment valign) : Label(text, halign, valign) {
}
string UntranslatedLabel::get_text() const {
return Label::m_text;
}
/* -------------------- Button -------------------- */
Button::Button() : m_activep (false), highlight (false) {
}
void Button::activate()
{
sound::EmitSoundEvent ("menuswitch");
m_activep = true;
invalidate();
}
void Button::deactivate() {
m_activep = false;
invalidate();
}
void Button::setHighlight(bool shouldHighlight) {
highlight = shouldHighlight;
invalidate();
}
bool Button::isHighlight() {
return highlight;
}
void Button::draw(ecl::GC &gc, const ecl::Rect &r) {
const int borderw = 4;
ecl::Surface *s = enigma::GetImage (m_activep ? "buttonhl" : "button");
if (s) { // Ugly, but hey, it works
Rect srcrect (0,0,borderw, borderw);
Rect area = get_area();
// background
if (highlight)
set_color (gc, 70, 70, 70);
else
set_color (gc, 0,0,0);
box (gc, smaller(area, borderw));
set_color (gc, 0,0,0);
// corners
blit (gc, area.x, area.y, s, srcrect);
srcrect.x += s->width()-borderw;
blit (gc, area.x+area.w-borderw, area.y, s, srcrect);
srcrect.x = 0;
srcrect.y += s->height()-borderw;
blit (gc, area.x, area.y+area.h-borderw, s, srcrect);
srcrect.x += s->width()-borderw;
blit (gc, area.x+area.w-borderw, area.y+area.h-borderw, s, srcrect);
// horizontal borders
{
int tilew = s->width() - 2*borderw;
int ntiles = (area.w - 2*borderw) / tilew;
int x = area.x + borderw;
for (int i=0; i<ntiles; ++i) {
blit (gc, x, area.y, s, Rect (borderw, 0, tilew, borderw));
blit (gc, x, area.y+area.h-borderw, s,
Rect (borderw, s->height()-borderw, tilew, borderw));
x += tilew;
}
int restw = (area.w - 2*borderw) - tilew*ntiles;
blit (gc, x, area.y, s, Rect (borderw, 0, restw, borderw));
blit (gc, x, area.y+area.h-borderw, s,
Rect (borderw, s->height()-borderw, restw, borderw));
}
// vertical borders
{
int tileh = s->height() - 2*borderw;
int ntiles = (area.h - 2*borderw) / tileh;
int y = area.y + borderw;
for (int i=0; i<ntiles; ++i) {
blit (gc, area.x, y, s, Rect (0, borderw, borderw, tileh));
blit (gc, area.x+area.w-borderw, y, s,
Rect (s->width()-borderw, borderw, borderw, tileh));
y += tileh;
}
int resth = (area.h - 2*borderw) - tileh*ntiles;
blit (gc, area.x, y, s, Rect (0, borderw, borderw, resth));
blit (gc, area.x+area.w-borderw, y, s,
Rect (s->width()-borderw, borderw, borderw, resth));
}
}
else {
set_color (gc, 0,0,0);
box (gc, r);
set_color (gc, 160,160,160);
frame (gc, r);
frame (gc, smaller(r, 1));
}
}
/* -------------------- PushButton -------------------- */
PushButton::PushButton() : m_pressedp (false) {
}
bool PushButton::on_event(const SDL_Event &e) {
Widget::on_event(e);
bool was_pressed = m_pressedp;
switch (e.type) {
case SDL_KEYDOWN:
if (e.key.keysym.sym != SDLK_RETURN &&
e.key.keysym.sym != SDLK_SPACE) break;
// fall-through
case SDL_MOUSEBUTTONDOWN:
m_pressedp = true;
break;
case SDL_KEYUP:
if (e.key.keysym.sym != SDLK_RETURN &&
e.key.keysym.sym != SDLK_SPACE &&
e.key.keysym.sym != SDLK_PAGEDOWN &&
e.key.keysym.sym != SDLK_PAGEUP) break;
lastUpSym = e.key.keysym.sym;
lastUpBotton = 0;
m_pressedp = false;
break;
case SDL_MOUSEBUTTONUP:
lastUpSym = SDLK_UNKNOWN;
lastUpBotton = e.button.button;
m_pressedp = false;
break;
}
bool changed = (was_pressed != m_pressedp);
if (changed) {
invalidate();
if (!m_pressedp) {
if (soundOk())
sound::EmitSoundEvent("menuok");
invoke_listener();
}
}
return changed;
}
void PushButton::deactivate() {
m_pressedp = false;
lastUpSym = SDLK_UNKNOWN;
lastUpBotton = 0;
invalidate();
Button::deactivate();
}
SDLKey PushButton::getLastUpSym() {
return lastUpSym;
}
Uint8 PushButton::getLastUpButton() {
return lastUpBotton;
}
bool PushButton::soundOk() {
return true;
}
/* -------------------- TextButton -------------------- */
ecl::Font *TextButton::menufont = 0;
ecl::Font *TextButton::menufont_pressed = 0;
TextButton::TextButton(ActionListener *al) {
if (menufont == 0) {
menufont = enigma::GetFont("menufont");
menufont_pressed = enigma::GetFont("menufontsel");
}
set_listener(al);
}
void TextButton::draw(ecl::GC &gc, const ecl::Rect &r) {
Button::draw(gc,r);
Font *f = is_pressed() ? menufont_pressed : menufont;
string text = get_text();
int h = f->get_height();
int w = f->get_width(text.c_str());
int x = get_x() + (get_w()-w)/2;
int y = get_y() + (get_h()-h)/2;
f->render (gc, x, y, text.c_str());
}
/* -------------------- StaticTextButton -------------------- */
StaticTextButton::StaticTextButton(const string &t, ActionListener *al)
: TextButton(al),
text(t)
{
}
void StaticTextButton::set_text(const std::string &t) {
if (t != text) {
text = t;
invalidate();
}
}
string StaticTextButton::get_text() const {
return _(text.c_str()); // translate
}
/* -------------------- UntranslatedStaticTextButton -------------------- */
UntranslatedStaticTextButton::UntranslatedStaticTextButton(const string &t,
ActionListener *al)
: StaticTextButton(t, al)
{
}
string UntranslatedStaticTextButton::get_text() const {
return StaticTextButton::text;
}
/* -------------------- Buttons for Options -------------------- */
BoolOptionButton::BoolOptionButton(const char *option_name,
const string& true_text, const string& false_text,
ActionListener *al)
: TextButton(al),
optionName(option_name),
trueText(true_text),
falseText(false_text)
{
}
bool BoolOptionButton::toggle() {
bool newval = !enigma_options::GetBool(optionName);
enigma_options::SetOption(optionName, newval);
invalidate();
return newval;
}
void BoolOptionButton::on_action(Widget *) {
toggle();
}
string BoolOptionButton::get_text() const {
return enigma_options::GetBool(optionName) ? _(trueText.c_str()) : _(falseText.c_str());
}
/* -------------------- ValueButton -------------------- */
ValueButton::ValueButton(int min_value_, int max_value_)
: TextButton(this),
min_value(min_value_),
max_value(max_value_)
{
}
void ValueButton::setMaxValue(int max) {
max_value = max;
}
void ValueButton::init() {
update_value(-1, get_value()); // fixes wrong values (e.g. from .enimarc)
}
bool ValueButton::inc_value(int offset) {
int old_value = get_value();
return update_value(old_value, old_value+offset);
}
string ValueButton::get_text() const {
return get_text(get_value());
}
bool ValueButton::update_value(int old_value, int new_value) {
new_value = Clamp(new_value, min_value, max_value);
if (new_value != old_value) {
set_value(new_value);
invalidate();
return true;
}
return false;
}
void ValueButton::on_action(Widget *) {
int incr = 1;
bool stop = false;
if (getLastUpSym() == SDLK_PAGEDOWN || getLastUpButton() == SDL_BUTTON_RIGHT ||
getLastUpButton() == 5) { // wheel down
incr = -1;
}
if (getLastUpSym() == SDLK_PAGEDOWN || getLastUpSym() == SDLK_PAGEUP ||
getLastUpButton() == SDL_BUTTON_RIGHT ||
getLastUpButton() == 4 || getLastUpButton() == 5) {
stop = true;
}
if (inc_value(incr)) {
sound::EmitSoundEvent("menuswitch");
} else {
if (stop) {
sound::EmitSoundEvent("menustop");
} else {
sound::EmitSoundEvent("menuswitch");
if (incr == 1)
update_value(get_value(), min_value);
else
update_value(get_value(), max_value);
}
}
}
bool ValueButton::soundOk() {
return false;
}
/* -------------------- ImageButton -------------------- */
ImageButton::ImageButton(const string &unselected,
const string &selected,
ActionListener *al)
: fname_sel(selected), fname_unsel(unselected)
{
set_listener(al);
}
void ImageButton::set_images(const string &unselected, const string &selected) {
fname_sel = selected;
fname_unsel = unselected;
}
void ImageButton::draw(ecl::GC &gc, const ecl::Rect &r) {
Button::draw(gc, r);
string &fname = is_pressed() ? fname_sel : fname_unsel;
if (Surface *s = enigma::GetImage(fname.c_str())) {
int w=s->width();
int h=s->height();
int x = get_x() + (get_w()-w)/2;
int y = get_y() + (get_h()-h)/2;
blit(gc, x, y, s);
}
}

View File

@@ -0,0 +1,465 @@
/*
* Copyright (C) 2002,2003 Daniel Heck
*
* 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 WIDGETS_HH_INCLUDED
#define WIDGETS_HH_INCLUDED
#include "ecl_fwd.hh"
#include "ecl_geom.hh"
#include "SDL.h"
namespace enigma { namespace gui {
/* -------------------- Alignment -------------------- */
enum HAlignment {
HALIGN_LEFT,
HALIGN_CENTER,
HALIGN_RIGHT
};
enum VAlignment {
VALIGN_TOP,
VALIGN_CENTER,
VALIGN_BOTTOM
};
/* -------------------- Events & Event Handlers -------------------- */
class Widget;
class ActionListener {
public:
virtual ~ActionListener() {}
virtual void on_action(Widget *) {};
};
/* -------------------- GUI Widgets -------------------- */
class Container;
class Widget : public ActionListener {
public:
/* ---------- Widget interface ---------- */
virtual void draw (ecl::GC &gc, const ecl::Rect &r) = 0;
virtual void activate() {}
virtual void deactivate() {}
virtual void realize (const ecl::Rect &r) {
set_area (r);
}
virtual bool on_event(const SDL_Event &/*e*/);
Uint8 lastMouseButton() {return mouseButton;}
SDLMod lastModifierKeys() { return modifierKeys; }
virtual void move (int x, int y);
virtual void resize (int w, int h);
virtual void naturalsize (int &w, int &h) const {
w = h = 5;
}
/* ---------- Accessors ---------- */
void set_size(int w, int h) {area.w = w; area.h = h;}
ecl::Rect get_area() const { return area; }
void set_area(const ecl::Rect &r) { area = r; }
int get_x() const { return area.x; }
int get_y() const { return area.y; }
int get_w() const { return area.w; }
int get_h() const { return area.h; }
void set_parent(Container *parent) { m_parent = parent; }
Container *get_parent () const { return m_parent; }
void set_listener(ActionListener *al) {
m_listener = al;
}
void invalidate();
virtual void tick (double /*dtime*/) {}
protected:
Widget(Container *parent=0);
/* ---------- Functions ---------- */
void reconfigure();
void invalidate_area(const ecl::Rect &r);
void invoke_listener();
private:
ecl::Rect area;
Container *m_parent;
ActionListener *m_listener;
SDLMod modifierKeys;
Uint8 mouseButton;
};
#if 0
/* -------------------- EmptyWidget -------------------- */
class EmptyWidget : public Widget {
public:
EmptyWidget () : Widget ()
{}
virtual void draw (ecl::GC &gc, const ecl::Rect &r)
{}
virtual void naturalsize (int &w, int &h) const {
w = h = 0;
}
};
#endif
/* -------------------- AreaManager -------------------- */
class AreaManaged {
public:
virtual ~AreaManaged() {}
virtual void invalidate_area(const ecl::Rect &r) = 0;
virtual void invalidate_all() = 0;
virtual void refresh() = 0;
};
// The AreaManager perform refreshes of invalidated regions.
// It gets attached to the top-level Container during the first invalidation
// or refresh request.
class AreaManager : public AreaManaged {
public:
AreaManager(Container *managed);
void invalidate_area(const ecl::Rect &r);
void invalidate_all();
void refresh();
private:
ecl::RectList dirtyrects;
Container *top_container;
};
/* -------------------- Container -------------------- */
class Container : public Widget, public AreaManaged {
public:
Container();
~Container();
void add_child (Widget *w);
virtual void remove_child (Widget *w);
virtual void exchange_child (Widget *oldChild, Widget *newChild);
virtual void reconfigure_child (Widget *w);
Widget *find_widget(int x, int y);
Widget *find_adjacent_widget(Widget *from, int x, int y);
void clear();
void draw_all();
// Widget interface.
void draw (ecl::GC& gc, const ecl::Rect &r);
void move (int x, int y);
// AreaManaged interface.
void invalidate_area(const ecl::Rect &r);
void invalidate_all();
void refresh();
protected:
typedef std::vector<Widget *> WidgetList;
typedef WidgetList::iterator iterator;
iterator begin() { return m_widgets.begin(); }
iterator end() { return m_widgets.end(); }
WidgetList m_widgets;
private:
ecl::Rect boundingbox();
AreaManager *getAreaManager();
AreaManager *managed_by;
};
/* -------------------- List, HList, VList -------------------- */
class List : public Container {
public:
void set_spacing (int pixels);
enum ExpansionMode {
EXPAND,
TIGHT
};
void add_back (Widget *w, ExpansionMode m = List::TIGHT);
virtual void remove_child (Widget *w);
virtual void exchange_child (Widget *oldChild, Widget *newChild);
void set_default_size (int w, int h);
void set_alignment (HAlignment halign, VAlignment valign);
virtual bool fits() = 0;
protected:
List(int spacing=5);
int calc_minimum_height () const;
int calc_minimum_width () const;
int get_spacing () const;
void get_size (const Widget *widget, int &w, int &h) const;
// ---------- Widget interface ----------
virtual void move (int x, int y);
virtual void resize(int w, int h);
// ---------- List interface ----------
virtual void recalc() = 0;
// ---------- Container interface ----------
virtual void reconfigure_child (Widget *w);
protected:
std::vector<ExpansionMode> m_expansionmodes;
private:
int m_spacing; // # of pixels between container items
bool has_default_size;
int defaultw, defaulth;
protected:
HAlignment m_halign;
VAlignment m_valign;
};
class HList : public List {
public:
HList() : List() {}
virtual bool fits();
private:
// List interface
virtual void recalc();
};
class VList : public List {
public:
VList() : List() {}
virtual bool fits();
private:
// List interface
virtual void recalc();
};
/* -------------------- Image -------------------- */
class Image : public Widget {
public:
Image (const std::string &iname) : imgname(iname) {}
void draw (ecl::GC &gc, const ecl::Rect &r);
private:
std::string imgname;
};
/* -------------------- Label -------------------- */
class Label : public Widget {
public:
Label (const std::string &text="",
HAlignment halign=HALIGN_CENTER,
VAlignment valign=VALIGN_CENTER);
// Widget interface
virtual void draw (ecl::GC &gc, const ecl::Rect &r);
virtual void naturalsize (int &w, int &h) const;
// Methods
void set_text (const std::string &text);
virtual std::string get_text() const; // translated
std::string getText() const;
void set_font (ecl::Font *font);
void set_alignment (HAlignment halign, VAlignment valign=VALIGN_CENTER);
bool text_fits(double area_fraction = 1.0);
protected:
// Variables.
std::string m_text;
ecl::Font *m_font;
HAlignment m_halign;
VAlignment m_valign;
};
/* -------------------- UntranslatedLabel -------------------- */
class UntranslatedLabel : public Label {
public:
UntranslatedLabel(const std::string &text="",
HAlignment halign=HALIGN_CENTER,
VAlignment valign=VALIGN_CENTER);
// TextButton interface.
virtual std::string get_text() const;
};
/* -------------------- Button -------------------- */
class Button : public Widget {
public:
void setHighlight(bool shouldHighlight);
bool isHighlight();
protected:
Button();
// Widget interface.
void draw(ecl::GC &gc, const ecl::Rect &r);
void activate();
void deactivate();
bool m_activep;
bool highlight;
};
/* -------------------- PushButton -------------------- */
class PushButton : public Button {
public:
PushButton();
bool is_pressed() { return m_pressedp; }
protected:
bool on_event(const SDL_Event &e);
void deactivate();
SDLKey getLastUpSym();
Uint8 getLastUpButton();
virtual bool soundOk();
private:
bool m_pressedp;
SDLKey lastUpSym;
Uint8 lastUpBotton;
};
/* -------------------- TextButton -------------------- */
class TextButton : public PushButton {
public:
TextButton(ActionListener *al=0);
virtual std::string get_text() const = 0;
private:
// Widget interface.
void draw(ecl::GC &gc, const ecl::Rect &r);
// Variables.
static ecl::Font *menufont, *menufont_pressed;
};
/* -------------------- StaticTextButton -------------------- */
class StaticTextButton : public TextButton {
public:
StaticTextButton(const std::string &t, ActionListener *al=0);
virtual void set_text(const std::string &t);
// TextButton interface.
std::string get_text() const;
protected:
// Variables.
std::string text;
};
/* -------------------- UntranslatedStaticTextButton -------------------- */
class UntranslatedStaticTextButton : public StaticTextButton {
public:
UntranslatedStaticTextButton(const std::string &t, ActionListener *al=0);
// TextButton interface.
std::string get_text() const;
};
/* -------------------- BoolOptionButton -------------------- */
class BoolOptionButton : public TextButton {
public:
BoolOptionButton(const char *option_name,
const std::string& true_text,
const std::string& false_text,
ActionListener *al = 0);
bool toggle(); // returns new value
void on_action(Widget *);
// TextButton interface.
std::string get_text() const;
private:
const char *optionName;
std::string trueText;
std::string falseText;
};
/* -------------------- ValueButton -------------------- */
class ValueButton: public TextButton {
public:
ValueButton(int min_value_, int max_value_);
virtual int get_value() const = 0;
virtual void set_value(int value) = 0;
void setMaxValue(int max);
bool inc_value(int offset);
// TextButton interface.
virtual std::string get_text() const;
// Widget interface.
virtual void on_action(Widget *w);
protected:
void init(); // called in ctor of derived
virtual bool soundOk();
private:
int min_value;
int max_value;
bool update_value(int old_value, int new_value);
virtual std::string get_text(int value) const = 0;
};
/* -------------------- ImageButton -------------------- */
class ImageButton : public PushButton {
public:
ImageButton(const std::string &unselected,
const std::string &selected,
ActionListener *al = 0);
void set_images(const std::string &unselected, const std::string &selected);
// Widget interface.
virtual void draw(ecl::GC &gc, const ecl::Rect &r);
private:
std::string fname_sel, fname_unsel;
};
}} // namespace enigma::gui
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 ITEMS_HH_INCLUDED
#define ITEMS_HH_INCLUDED
#include "objects_decl.hh"
namespace world
{
enum ItemID {
it_INVALID = -1,
it_FIRST = 0,
it_none = 0,
it_1pkillstone,
it_2pkillstone,
it_abyss,
it_bag,
it_banana,
it_blackbomb,
it_blackbomb_burning,
it_blocker,
it_booze,
it_brake,
it_bridge_oxyd,
it_bridge_oxyd_active,
it_booze_broken,
it_brush,
it_burnable,
it_burnable_fireproof,
it_burnable_ignited,
it_burnable_burning,
it_burnable_ash,
it_burnable_oil,
it_changefloor,
it_cherry,
it_cherry_crushed,
it_coffee,
it_coin1,
it_coin2,
it_coin4,
it_crack0,
it_crack1,
it_crack2,
it_crack3,
it_cross,
it_death,
it_debris,
it_document,
it_drop,
it_dynamite,
it_dummy,
it_easykeepstone,
it_easykillstone,
it_explosion1,
it_explosion2,
it_explosion3,
it_extinguisher,
it_extinguisher_medium,
it_extinguisher_empty,
it_extralife,
it_flagblack,
it_flagwhite,
it_floppy,
it_glasses,
it_glasses_broken,
it_hammer,
it_hill,
it_hollow,
it_hstrip,
it_inversesensor,
it_key_a,
it_key_b,
it_key_c,
it_landmine,
it_laserbeam,
it_magicwand,
it_magnet_off,
it_magnet_on,
it_odometer,
it_oxyd5f,
it_pencil,
it_pin,
it_pipe_e, it_pipe_w, it_pipe_s, it_pipe_n,
it_pipe_es, it_pipe_ne, it_pipe_sw, it_pipe_wn,
it_pipe_h, it_pipe_v,
it_puller_n,
it_puller_e,
it_puller_s,
it_puller_w,
it_ring,
it_rubberband,
it_seed,
it_seed_nowood,
it_seed_volcano,
it_sensor,
it_shogun_s,
it_shogun_m,
it_shogun_l,
it_signalfilter0,
it_signalfilter1,
it_spade,
it_spoon,
it_spring1,
it_spring2,
it_springboard,
it_squashed,
it_surprise,
it_sword,
it_tinyhill,
it_tinyhollow,
it_trigger,
it_umbrella,
it_vortex_open,
it_vortex_closed,
it_vstrip,
it_weight,
it_whitebomb,
it_wormhole_off,
it_wormhole_on,
it_wrench,
it_yinyang,
it_LAST,
it_COUNT
};
/*! What may happen to an item _after_ it was activated? */
enum ItemAction {
ITEM_DROP, //!< Drop it to the floor
ITEM_KILL, //!< Remove it from the inventory and dispose it
ITEM_KEEP, //!< Keep it in the inventory; do nothing further
};
enum ItemFlags {
itf_none = 0,
itf_static = 1, //!< Cannot be picked up
itf_indestructible = 2, //!< Cannot be destroyed by explosions etc.
itf_animation = 4, //!< Use set_anim() instead of set_model()
itf_invisible = 8, //!< Item has no visible model
itf_inflammable = 16, //!< Burns when hit by laser beam
itf_norespawn = 32, //!< Don't respawn balls on top of this item
itf_fireproof = 64, //!< This item can't burn by fire
};
struct ItemTraits {
const char *name; //!< Name of the item, e.g., "it-hammer"
ItemID id;
int flags; //!< Combination of ItemFlags
float radius; //!< Radius, 0.0 = default
};
class Item : public GridObject {
public:
Item();
/* ---------- Public methods ---------- */
void kill();
void replace (ItemID id);
/* ---------- Virtual functions ---------- */
const char *get_kind() const;
void init_model();
void on_laserhit(Direction);
/* ---------- Item interface ---------- */
virtual Item *clone() = 0;
virtual const ItemTraits &get_traits() const = 0;
/*! Return true if item completely covers the floor. In this
case the Floor::actor_contact() will not be called
automatically; this must be done from `Item::actor_hit' (if
at all). */
virtual bool covers_floor(ecl::V2 pos) const { return false; }
/*! Return the force an item exerts on actor `a'. This is
used by sloped and hills for force fields that are local to
the current field. For global force fields you have to
register a ForceField in the world. */
virtual void add_force(Actor *a, ecl::V2 &f);
virtual bool can_drop_at (GridPos p);
virtual void drop (Actor *a, GridPos p);
/*! Called when item is dropped by actor `a' */
virtual void on_drop(Actor *a);
/*! Called when item is picked up by actor `a' */
virtual void on_pickup(Actor *a);
/*! Called when stone above item changes. */
virtual void stone_change(Stone *st);
/*! Called when item is ``hit'' by a moving stone. */
virtual void on_stonehit(Stone *st);
/*! Called when item is ``hit'' by an actor. Return true if
the item should be picked up. */
virtual bool actor_hit(Actor *a);
/*! The model used for displaying this item in an
inventory. */
virtual string get_inventory_model();
/* Called when item is activated by the owner of `a'. */
virtual ItemAction activate(Actor* a, GridPos p);
protected:
// GridObject interface
virtual void set_model (const std::string &mname) {
display::SetModel(GridLoc(GRID_ITEMS, get_pos()), mname);
}
virtual display::Model *get_model () {
return display::GetModel(GridLoc(GRID_ITEMS, get_pos()));
}
virtual void kill_model (GridPos p) {
display::KillModel (GridLoc (GRID_ITEMS, p));
}
// replace template method hook
virtual void setup_successor(Item *newitem) {}
};
/* -------------------- Inline functions -------------------- */
/*! Return unique item type identifier. */
inline ItemID get_id (Item *it) {
if (it)
return it->get_traits().id;
return it_none;
}
inline bool has_flags (Item *it, ItemFlags flags) {
return (it->get_traits().flags & flags) == flags;
}
/* -------------------- Functions -------------------- */
void InitItems();
}
#endif

View File

@@ -0,0 +1,797 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 "laser.hh"
#include "sound.hh"
#include "stones_internal.hh"
#include "server.hh"
#include <algorithm>
#include <cassert>
#include <map>
using namespace std;
using namespace world;
using namespace lasers;
using stones::maybe_push_stone;
using ecl::V2;
namespace
{
/* -------------------- LaserBeam -------------------- */
class LaserBeam : public Item, public LaserEmitter {
public:
static void emit_from(GridPos p, Direction d);
static void kill_all();
static void all_emitted();
// LaserEmitter interface
DirectionBits emission_directions() const { return directions; }
static ItemTraits traits;
const ItemTraits &get_traits() const {
return traits;
}
private:
LaserBeam(Direction dir) {
directions = to_bits(dir);
}
// Item interface.
void on_laserhit(Direction dir);
void on_creation (GridPos p);
void init_model();
bool actor_hit(Actor *actor);
Item *clone() {
// new LaserBeams may only created inside `emit_from'.
assert(0);
return 0;
}
void dispose();
// Variables
DirectionBits directions;
static vector<LaserBeam*> instances;
static map<GridPos, int> old_laser_positions;
};
ItemTraits LaserBeam::traits = {"it-laserbeam", it_laserbeam,
itf_static | itf_indestructible, 0.0 };
/* -------------------- Laser Stones -------------------- */
/** \page st-laser Laser Stone
These stones emit a laser beam in a specified direction when
activated. They are the only objects in the game that can act as
primary light sources (mirrors can also emit light but they require
an incoming beam).
\subsection lasera Attributes
- \b on: 1 if laser in active, 0 if not
- \b dir: the Direction in which light is emitted
(NORTH, EAST, SOUTH, WEST)
\subsection laserm Messages
- \b on, \b off, \b onoff: as usual
\subsection lasersa See also
\ref st-pmirror, \ref st-3mirror
*/
class LaserStone : public LaserEmitter, public stones::OnOffStone {
public:
LaserStone (Direction dir=EAST);
static void reemit_all();
private:
// INSTANCELISTOBJ(LaserStone);
// We can't use this macro here: g++ can't handle multiple inheritance
// and covariant return types at the same time ("sorry, not
// implemented: ..." first time I ever saw this error message :-)
typedef std::vector<LaserStone*> InstanceList;
static InstanceList instances;
Stone *clone() {
LaserStone *o = new LaserStone(*this);
instances.push_back(o);
return o;
}
void dispose() {
instances.erase(find(instances.begin(), instances.end(), this));
delete this;
}
// LaserEmitter interface
DirectionBits emission_directions() const;
// OnOffStone interface.
void notify_onoff(bool on);
// Private methods.
void emit_light();
Direction get_dir() const {return Direction(int_attrib("dir"));}
// Stone interface.
void on_creation (GridPos p);
void init_model();
};
}
/* -------------------- PhotoCell -------------------- */
vector<void*> PhotoCell::instances;
PhotoCell::~PhotoCell()
{
photo_deactivate();
}
/**
* This function notifies all instances of PhotoCell that a
* recalculation of the laser beams is about to begin by calling
* on_recalc_start() for each instance.
*/
void PhotoCell::notify_start()
{
for(unsigned i=0; i<instances.size(); ++i)
{
PhotoCell *pc = (PhotoCell*) instances[i];
pc->on_recalc_start();
}
}
/**
* This function notifies all instances of PhotoCell that the engine
* has finished recalculating the laser beams by calling
* on_recalc_finish() for each instance.
*/
void PhotoCell::notify_finish()
{
for(unsigned i=0; i<instances.size(); ++i)
{
PhotoCell *pc = (PhotoCell*) instances[i];
pc->on_recalc_finish();
}
}
void PhotoCell::photo_activate()
{
vector<void*>::iterator i = std::find(instances.begin(), instances.end(), this);
if (i != instances.end())
assert (0 || "Photocell activated twice\n");
else
instances.push_back (this);
}
void PhotoCell::photo_deactivate()
{
vector<void*>::iterator i;
i = std::find (instances.begin(), instances.end(), this);
if (i != instances.end())
instances.erase(i);
}
/* -------------------- PhotoStone -------------------- */
PhotoStone::PhotoStone(const char *kind) : Stone(kind)
{
illuminated = false;
}
void PhotoStone::on_recalc_start()
{}
void PhotoStone::on_recalc_finish()
{
GridPos p = get_pos();
bool illu = (LightFrom(p, NORTH) || LightFrom(p, EAST)
|| LightFrom(p, WEST) || LightFrom(p, SOUTH));
if (illu != illuminated) {
if (illu) notify_laseron();
else notify_laseroff();
illuminated = illu;
}
}
/* -------------------- LaserBeam -------------------- */
// The implementation of laser beams is a little tricky because, in
// spite of being implemented as Items, lasers aren't localized and
// changes to any part of the beam can affect the beam elsewhere. A
// `change' may be anything from moving a stone in or out of the beam,
// rotating or moving one of the mirrors, to making a stone in the
// beam transparent.
//
// Here are a couple of facts about laser beams in Enigma:
//
// - Laser beams are static. Once calculated they do not change until
// they are completely recalculated
//
// - LaserBeam::emit_from() is the only way to emit laser beams. A new
// beam will propagate automatically and stops only if it comes
// across an item or a stone that returns `false' from
// Stone::is_transparent().
//
// - `on_laserhit()' is called for objects in the beam *whenever*
// the beam is recalculated. For objects that need to be notified
// when the laser goes on or off, use the `PhotoStone'
// mixin.
vector<LaserBeam*> LaserBeam::instances;
map<GridPos, int> LaserBeam::old_laser_positions;
void LaserBeam::init_model()
{
if (directions & (EASTBIT | WESTBIT)) {
if (directions & (NORTHBIT | SOUTHBIT))
set_model("it-laserhv");
else
set_model("it-laserh");
}
else if (directions & (NORTHBIT | SOUTHBIT))
set_model("it-laserv");
}
void LaserBeam::on_laserhit(Direction dir)
{
DirectionBits dirbit = to_bits(dir);
if (!(directions & dirbit)) {
// `dir' not in `directions' ?
directions = DirectionBits(directions | dirbit);
emit_from(get_pos(), dir);
init_model();
}
}
void LaserBeam::on_creation (GridPos p)
{
if (directions & EASTBIT) emit_from(p, EAST);
if (directions & WESTBIT) emit_from(p, WEST);
if (directions &NORTHBIT) emit_from(p, NORTH);
if (directions &SOUTHBIT) emit_from(p, SOUTH);
init_model();
}
void LaserBeam::emit_from(GridPos p, Direction dir)
{
bool may_pass = true;
p.move(dir);
if (Stone *st = GetStone(p)) {
may_pass = st->is_transparent (dir);
st->on_laserhit (dir);
}
if (may_pass) {
if (Item *it = GetItem(p))
it->on_laserhit (dir);
else {
LaserBeam *lb = new LaserBeam (dir);
SetItem(p, lb);
instances.push_back(lb);
}
}
}
bool LaserBeam::actor_hit(Actor *actor)
{
double r = get_radius(actor);
V2 p = actor->get_pos();
GridPos gp = get_pos();
// distance of actor from center of the grid
double dx = fabs(p[0] - gp.x - 0.5) - r;
double dy = fabs(p[1] - gp.y - 0.5) - r;
if ((directions & (EASTBIT | WESTBIT) && dy<-0.1) ||
(directions & (NORTHBIT | SOUTHBIT)) && dx<-0.1)
{
SendMessage(actor, "laserhit");
}
return false; // laser beams can't be picked up
}
void LaserBeam::kill_all()
{
assert(old_laser_positions.empty());
while (!instances.empty())
{
LaserBeam *lb = instances[0];
GridPos pos = lb->get_pos();
old_laser_positions[pos] = static_cast<int>(lb->directions);
world::KillItem(pos);
}
}
void LaserBeam::all_emitted()
{
vector<LaserBeam*>::const_iterator end = instances.end();
map<GridPos, int>::iterator none = old_laser_positions.end();
double x = 0, y = 0;
int count = 0;
for (vector<LaserBeam*>::const_iterator i = instances.begin(); i != end; ++i) {
LaserBeam *lb = *i;
GridPos pos = lb->get_pos();
map<GridPos, int>::iterator found = old_laser_positions.find(pos);
if (found != none) {
// a beam was at the current position (during last kill_all())
DirectionBits old_dir = static_cast<DirectionBits>(found->second);
if ((old_dir&lb->directions) != lb->directions) {
// a beam has been added here
x += pos.x;
y += pos.y;
++count;
}
}
else {
// store newly created LaserBeams
x += pos.x;
y += pos.y;
++count;
}
}
if (count) {
sound::EmitSoundEvent ("laseron", ecl::V2(x/count+.5, y/count+.5),
getVolume("laseron", NULL));
}
old_laser_positions.clear();
}
void LaserBeam::dispose()
{
instances.erase(std::find(instances.begin(), instances.end(), this));
delete this;
}
//----------------------------------------
// Laser stone
//----------------------------------------
LaserStone::InstanceList LaserStone::instances;
LaserStone::LaserStone (Direction dir)
: OnOffStone("st-laser")
{
set_attrib("dir", Value(dir));
}
DirectionBits
LaserStone::emission_directions() const
{
if (is_on()) {
return to_bits(get_dir());
}
return NODIRBIT;
}
void LaserStone::reemit_all()
{
for (unsigned i=0; i<instances.size(); ++i)
{
LaserStone *ls = (LaserStone*) instances[i];
ls->emit_light();
}
}
void LaserStone::notify_onoff(bool /*on*/)
{
RecalcLight();
}
void LaserStone::emit_light()
{
if (is_on())
LaserBeam::emit_from(get_pos(), get_dir());
}
void LaserStone::on_creation (GridPos p)
{
if (is_on())
RecalcLight();
Stone::on_creation(p);
}
void LaserStone::init_model()
{
string mname = is_on() ? "st-laseron" : "st-laser";
mname += to_suffix(get_dir());
set_model(mname);
}
/* -------------------- MirrorStone -------------------- */
namespace
{
class MirrorStone
: public Stone, public LaserEmitter, public PhotoCell
{
protected:
MirrorStone(const char *name, bool movable=false, bool transparent=false);
bool is_transparent() const { return int_attrib("transparent") != 0; }
bool is_movable() const { return int_attrib("movable") != 0; }
void set_orientation(int o) { set_attrib("orientation", o); }
int get_orientation() { return int_attrib("orientation"); }
void emit_light(Direction dir) {
if (!has_dir(outdirs, dir))
{
outdirs = DirectionBits(outdirs | to_bits(dir));
LaserBeam::emit_from(get_pos(), dir);
}
}
void init_model();
private:
// Object interface.
virtual Value message(const string &m, const Value &);
// LaserEmitter interface
DirectionBits emission_directions() const {
return outdirs;
}
// PhotoCell interface
void on_recalc_start() { outdirs = NODIRBIT; }
void on_recalc_finish() {}
// Stone interface
void actor_hit(const world::StoneContact &sc);
void on_creation (GridPos p);
void on_removal (GridPos p);
bool is_transparent(Direction) const { return is_transparent(); }
// Private methods
void rotate_right();
// Variables
DirectionBits outdirs;
};
}
MirrorStone::MirrorStone(const char *name, bool movable, bool transparent)
: Stone(name), outdirs(NODIRBIT)
{
set_attrib("transparent", transparent);
set_attrib("movable", movable);
set_attrib("orientation", Value(1));
}
void MirrorStone::init_model() {
string mname = get_kind();
mname += is_movable() ? "-m" : "-s";
mname += is_transparent() ? "t" : "o";
mname += char('0' + get_orientation());
set_model(mname);
}
Value MirrorStone::message(const string &m, const Value &val) {
if (m == "trigger" || m=="turn") {
rotate_right();
}
else if (m == "signal") {
if (to_double(val) != 0) {
rotate_right();
}
}
else if (m == "mirror-north") {
set_orientation(3);
init_model();
MaybeRecalcLight(get_pos());
}
else if (m == "mirror-east") {
set_orientation(4);
init_model();
MaybeRecalcLight(get_pos());
}
else if (m == "mirror-south") {
set_orientation(1);
init_model();
MaybeRecalcLight(get_pos());
}
else if (m == "mirror-west") {
set_orientation(2);
init_model();
MaybeRecalcLight(get_pos());
}
return Value();
}
void MirrorStone::actor_hit(const world::StoneContact &sc)
{
if (is_movable())
maybe_push_stone(sc);
rotate_right();
}
void MirrorStone::on_creation (GridPos p)
{
photo_activate();
Stone::on_creation(p);
}
void MirrorStone::on_removal(GridPos p)
{
photo_deactivate();
Stone::on_removal(p);
}
void MirrorStone::rotate_right()
{
set_orientation(1+(get_orientation() % 4));
init_model();
MaybeRecalcLight(get_pos());
sound_event ("mirrorturn");
}
/* -------------------- Plane Mirror -------------------- */
namespace
{
class PlaneMirror : public MirrorStone {
CLONEOBJ(PlaneMirror);
public:
PlaneMirror(char orientation='/', bool movable=false, bool transparent=false)
: MirrorStone("st-pmirror", movable, transparent)
{
SetOrientation(orientation);
}
private:
void SetOrientation(char o) {
const char *a = " -\\|/";
MirrorStone::set_orientation(int (strchr(a,o)-a));
}
char GetOrientation() {
const char *a = " -\\|/";
return a[MirrorStone::get_orientation()];
}
void on_laserhit(Direction dir);
};
}
void PlaneMirror::on_laserhit(Direction dir)
{
char orientation = GetOrientation();
bool transparent = is_transparent();
switch (orientation) {
case '|':
if (dir==EAST || dir==WEST) {
emit_light(reverse(dir));
if (transparent)
emit_light(dir);
}
else if ((dir == NORTH || dir == SOUTH) && transparent &&
server::GameCompatibility == GAMET_OXYD1) {
emit_light(dir);
}
break;
case '-':
if (dir==NORTH || dir==SOUTH) {
emit_light(reverse(dir));
if (transparent)
emit_light(dir);
}
else if ((dir == EAST || dir == WEST) && transparent &&
server::GameCompatibility == GAMET_OXYD1) {
emit_light(dir);
}
break;
case '/':
switch(dir) {
case EAST: emit_light(NORTH); break;
case SOUTH: emit_light(WEST); break;
case NORTH: emit_light(EAST); break;
case WEST: emit_light(SOUTH); break;
case NODIR: break;
}
if (transparent)
emit_light(dir);
break;
case '\\':
switch(dir) {
case EAST: emit_light(SOUTH); break;
case SOUTH: emit_light(EAST); break;
case NORTH: emit_light(WEST); break;
case WEST: emit_light(NORTH); break;
case NODIR: break;
}
if (transparent)
emit_light(dir);
break;
}
}
/* -------------------- TriangleMirror -------------------- */
namespace
{
// The orientations of the TriangleMirror have an unusual definition,
// but we cannot change them w/o changing many levels
//
// Flat side of the triangle
// points to : Orientation :
//
// WEST 4
// SOUTH 3
// EAST 2
// NORTH 1
class TriangleMirror : public MirrorStone {
CLONEOBJ(TriangleMirror);
public:
TriangleMirror(char orientation='v', bool movable=false, bool transparent=false)
: MirrorStone("st-3mirror", movable, transparent)
{
SetOrientation (orientation);
}
private:
void SetOrientation(char o) {
const char *a = " v<^>";
MirrorStone::set_orientation( int (strchr(a,o)-a));
}
Direction GetOrientation() // orientation of the flat side of the mirror
{
const Direction a[] = {NODIR, NORTH, EAST, SOUTH, WEST};
return a[MirrorStone::get_orientation()];
}
void on_laserhit (Direction dir);
};
}
void TriangleMirror::on_laserhit(Direction beam_dir)
// note: 'beam_dir' is the direction where laserbeam goes to
{
// direction where flat side of triangle points to
Direction flat_dir = GetOrientation();
Direction tip_dir = reverse(flat_dir);
if (beam_dir == tip_dir) // beam hits the flat side
emit_light(flat_dir);
else if (beam_dir == flat_dir) {
// this is the "complicated" case where the light falls
// on the tip of the triangle
switch (beam_dir) {
case SOUTH: case NORTH:
emit_light(EAST); emit_light(WEST); break;
case WEST: case EAST:
emit_light(SOUTH); emit_light(NORTH); break;
case NODIR: break;
}
} else
emit_light(tip_dir);
if (is_transparent())
emit_light(beam_dir);
}
//----------------------------------------------------------------------
// FUNCTIONS
//----------------------------------------------------------------------
namespace
{
/* This flag is true iff all lasers should be recalculated at the
end of the next tick. */
bool light_recalc_scheduled = false;
}
void lasers::Init() {
Register (new LaserStone);
Register ("st-laser-n", new LaserStone(NORTH));
Register ("st-laser-e", new LaserStone(EAST));
Register ("st-laser-s", new LaserStone(SOUTH));
Register ("st-laser-w", new LaserStone(WEST));
Register (new TriangleMirror);
Register ("st-mirror-3v", new TriangleMirror('v'));
Register ("st-mirror-3<", new TriangleMirror('<'));
Register ("st-mirror-3^", new TriangleMirror('^'));
Register ("st-mirror-3>", new TriangleMirror('>'));
Register ("st-mirror-3vm", new TriangleMirror('v', true));
Register ("st-mirror-3<m", new TriangleMirror('<', true));
Register ("st-mirror-3^m", new TriangleMirror('^', true));
Register ("st-mirror-3>m", new TriangleMirror('>', true));
Register ("st-mirror-3vt", new TriangleMirror('v', false, true));
Register ("st-mirror-3<t", new TriangleMirror('<', false, true));
Register ("st-mirror-3^t", new TriangleMirror('^', false, true));
Register ("st-mirror-3>t", new TriangleMirror('>', false, true));
Register ("st-mirror-3vtm", new TriangleMirror('v', true, true));
Register ("st-mirror-3<tm", new TriangleMirror('<', true, true));
Register ("st-mirror-3^tm", new TriangleMirror('^', true, true));
Register ("st-mirror-3>tm", new TriangleMirror('>', true, true));
Register (new PlaneMirror);
Register ("st-mirror-p|", new PlaneMirror('|'));
Register ("st-mirror-p/", new PlaneMirror('/'));
Register ("st-mirror-p-", new PlaneMirror('-'));
Register ("st-mirror-p\\", new PlaneMirror('\\'));
Register ("st-mirror-p|m", new PlaneMirror('|', true));
Register ("st-mirror-p/m", new PlaneMirror('/', true));
Register ("st-mirror-p-m", new PlaneMirror('-', true));
Register ("st-mirror-p\\m", new PlaneMirror('\\', true));
Register ("st-mirror-p|t", new PlaneMirror('|', false, true));
Register ("st-mirror-p/t", new PlaneMirror('/', false, true));
Register ("st-mirror-p-t", new PlaneMirror('-', false, true));
Register ("st-mirror-p\\t", new PlaneMirror('\\', false, true));
Register ("st-mirror-p|tm", new PlaneMirror('|', true, true));
Register ("st-mirror-p/tm", new PlaneMirror('/', true, true));
Register ("st-mirror-p-tm", new PlaneMirror('-', true, true));
Register ("st-mirror-p\\tm", new PlaneMirror('\\', true, true));
}
void lasers::MaybeRecalcLight(GridPos p) {
light_recalc_scheduled |=
(LightFrom(p, NORTH) || LightFrom(p, SOUTH) ||
LightFrom(p, WEST) || LightFrom(p, EAST));
}
void lasers::RecalcLight() {
light_recalc_scheduled = true;
}
bool lasers::LightFrom (GridPos p, Direction dir) {
p.move(dir);
if (LaserEmitter *le = dynamic_cast<LaserEmitter*>(GetStone(p)))
if (has_dir(le->emission_directions(), reverse(dir)))
return true;
if (LaserEmitter *le = dynamic_cast<LaserEmitter*>(GetItem(p)))
return (has_dir(le->emission_directions(), reverse(dir)));
return false;
}
void lasers::RecalcLightNow() {
if (light_recalc_scheduled) {
PhotoCell::notify_start();
LaserBeam::kill_all();
LaserStone::reemit_all();
PhotoCell::notify_finish();
LaserBeam::all_emitted();
light_recalc_scheduled = false;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2002,2003,2004 Daniel Heck
*
* 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 LASER_HH
#define LASER_HH
/* This file contains the declarations for (almost) everything that
has to do with lasers. */
#include "objects.hh"
namespace world
{
/**
* This interface must be implemented by all items and stones that
* are capable of emitting light.
*/
class LaserEmitter {
public:
virtual ~LaserEmitter() {}
virtual DirectionBits emission_directions() const = 0;
};
/* -------------------- PhotoCell -------------------- */
/**
* PhotoCells are objects (not necessarily stones) that are
* sensitive to laser light. Whenever the game engine
* recalculates the laser beams, instances of this class are
* notified about the beginning and the end of a recalculation.
*/
class PhotoCell {
public:
virtual ~PhotoCell();
// ---------- Static functions ----------
static void notify_start();
static void notify_finish();
// ---------- PhotoCell interface ----------
virtual void on_recalc_start() = 0;
virtual void on_recalc_finish() = 0;
protected:
/*! Derived classes must call this method to register
themselves for the on_recalc_start() and on_recalc_finish()
events. */
void photo_activate();
/*! Derived classes must call this method to unregister
themselves. It is automatically called by ~PhotoCell(), but
objects may have to call it explicitly if they are not
interested in PhotoCell events. */
void photo_deactivate();
private:
static std::vector<void*> instances;
};
/* -------------------- PhotoStone -------------------- */
/*! Most stones are indifferent to laser beams: They either block
the light completely or they let it pass, but they do not change
their internal state when they are hit by light. Certain kinds
of stones need to be notified whenever the `light' goes on or off
-- these can be derived from this class.
The most prominent example are Oxyd stones -- they open when
they are hit by a laser beam. See the remarks at the beginning
of this file to understand why overriding `on_laserhit' is not
sufficient for a proper implementation of Oxyd stones.
*/
class PhotoStone : public Stone, public PhotoCell {
protected:
PhotoStone (const char *kind);
private:
bool illuminated;
// PhotoCell interface
void on_recalc_start();
void on_recalc_finish();
// PhotoStone interface
virtual void notify_laseron() = 0;
virtual void notify_laseroff() = 0;
};
}
/* -------------------- Functions -------------------- */
namespace lasers
{
void Init();
/*! This function must be called at the end of each tick; it
recalculates the laser beams if necessary. */
void RecalcLightNow();
/*! Force all light beams to be recalculated at the end of the
current tick. So far, this is only used by laser stones and in
world::InitWorld(). */
void RecalcLight();
/*! If position `p' is inside a laser beam, force all laser beams
to be recalculated. This is mainly used when items and stones
are created or removed, but it can be also used for objects
(like doors) that sometimes shut off a light beam (when the door
is closed) and sometimes don't (when the door is open). */
void MaybeRecalcLight (enigma::GridPos p);
/*! Return true iff a stone or an item at position `p' it hit by
light coming from direction `dir'. */
bool LightFrom (enigma::GridPos p, enigma::Direction dir);
}
#endif

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

Some files were not shown because too many files have changed in this diff Show More