mirror of
https://github.com/ENSL/NS.git
synced 2024-11-15 09:21:54 +00:00
c2cd59fd98
Co-authored-by: Petter Rønningen <petter.ronningen@sintef.no>
1059 lines
34 KiB
C++
1059 lines
34 KiB
C++
#ifdef BALANCE_ENABLED
|
|
#pragma message( "[CONFIG] Balance system enabled" )
|
|
#else
|
|
#pragma message( "[CONFIG] Balance system disabled" )
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <vector>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "Balance.h"
|
|
|
|
using std::auto_ptr;
|
|
using std::ifstream;
|
|
using std::map;
|
|
using std::ofstream;
|
|
using std::set;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
//CONSIDER : use boost shared pointers for BalanceValueContainer map
|
|
//CONSIDER : put wrapper around std::map to include notification
|
|
// functionality and minimal change assignment operator; this would
|
|
// eliminate some heavy redundancy in code below.
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// BalanceListenerSort - functor that allows BalanceChangeListeners to be
|
|
// placed into sorted sets.
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
struct BalanceListenerSort
|
|
{
|
|
bool operator()(const BalanceChangeListener* lhs, const BalanceChangeListener* rhs) const
|
|
{ return lhs->getBalanceChangeListenerID() < rhs->getBalanceChangeListenerID(); }
|
|
bool operator()(const BalanceChangeListener* lhs, const unsigned int rhs) const
|
|
{ return lhs->getBalanceChangeListenerID() < rhs; }
|
|
bool operator()(const unsigned int lhs, const BalanceChangeListener* rhs) const
|
|
{ return lhs < rhs->getBalanceChangeListenerID(); }
|
|
};
|
|
|
|
typedef set<const BalanceChangeListener*,BalanceListenerSort> BalanceListenerSet; //storage
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// BalanceSystem - singleton that forms foundation of
|
|
// BalanceValueCollectionFactory functionality and tracks BalanceValueListener
|
|
// IDs, which allow deletion of BalanceChangeListeners without decoupling from
|
|
// each BalanceValueContainer they listen to.
|
|
//
|
|
// This is the only code in the new balance system that is sensitive to
|
|
// AVH_PLAYTEST_BUILD; it will return a dummy BalanceValueContainer and ignore
|
|
// BalanceChangeListener registration if AVH_PLAYTEST_BUILD is not #define'd.
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
#define g_BalanceSystem BalanceSystem::getInstance() //shorthand for convenience
|
|
|
|
class BalanceSystem
|
|
{
|
|
public:
|
|
static BalanceSystem& getInstance(void);
|
|
~BalanceSystem(void);
|
|
|
|
const string& getDefaultFilename(void);
|
|
unsigned int getNextListenerID(void);
|
|
|
|
BalanceValueContainer* getContainer(const string& filename);
|
|
|
|
void registerListener(const BalanceChangeListener* listener);
|
|
void unregisterListener(const BalanceChangeListener* listener);
|
|
const BalanceChangeListener* getListener(const unsigned int id);
|
|
|
|
private:
|
|
BalanceSystem(void);
|
|
|
|
unsigned int nextID;
|
|
map<string,BalanceValueContainer*> containersByFilename;
|
|
map<unsigned int,const BalanceChangeListener*> listeners;
|
|
string defaultFilename;
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// NullValueContainer - no-op implmentation of BalanceValueCollection used
|
|
// when playtest code is disabled (note that the macros never call into this,
|
|
// but it provides a sink for directly written calls
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
class NullValueContainer : public BalanceValueContainer
|
|
{
|
|
public:
|
|
NullValueContainer(void) {}
|
|
virtual ~NullValueContainer(void) {}
|
|
virtual BalanceValueContainer& operator=(const BalanceValueContainer& other) { return *this; }
|
|
virtual bool operator!=(const BalanceValueContainer& other) const { return !operator==(other); }
|
|
virtual bool operator==(const BalanceValueContainer& other) const { return (dynamic_cast<const NullValueContainer*>(&other) != NULL); }
|
|
virtual bool load(void) { return false; }
|
|
virtual bool save(void) { return false; }
|
|
virtual const float get(const string& name, const float default_value) const { return default_value; }
|
|
virtual const int get(const string& name, const int default_value) const { return default_value; }
|
|
virtual const string get(const string& name, const string& default_value) const { return default_value; }
|
|
virtual const BalanceFloatCollection* getFloatMap(void) const { return &null_float_map; }
|
|
virtual const BalanceIntCollection* getIntMap(void) const { return &null_int_map; }
|
|
virtual const BalanceStringCollection* getStringMap(void) const { return &null_string_map; }
|
|
virtual void insert(const string& name, const float value) {}
|
|
virtual void insert(const string& name, const int value) {}
|
|
virtual void insert(const string& name, const string& value) {}
|
|
virtual void remove(const string& name) {}
|
|
virtual void clear(void) {}
|
|
virtual void addListener(const BalanceChangeListener* listener) {}
|
|
virtual void addListener(const string& name, const BalanceChangeListener* listener) {}
|
|
virtual void removeListener(const BalanceChangeListener* listener) {}
|
|
virtual void removeListener(const string& name, const BalanceChangeListener* listener) {}
|
|
private:
|
|
const static BalanceFloatCollection null_float_map;
|
|
const static BalanceIntCollection null_int_map;
|
|
const static BalanceStringCollection null_string_map;
|
|
};
|
|
|
|
const BalanceFloatCollection NullValueContainer::null_float_map;
|
|
const BalanceIntCollection NullValueContainer::null_int_map;
|
|
const BalanceStringCollection NullValueContainer::null_string_map;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// StandardValueContainer - basic implementation of BalanceValueCollection
|
|
// that forms concrete base for other subclasses
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
typedef set<unsigned int> BalanceListenerIDSet; //handle collection
|
|
|
|
class StandardValueContainer : public BalanceValueContainer
|
|
{
|
|
public:
|
|
StandardValueContainer(void);
|
|
virtual ~StandardValueContainer(void);
|
|
virtual BalanceValueContainer& operator=(const BalanceValueContainer& other);
|
|
virtual bool operator!=(const BalanceValueContainer& other) const;
|
|
virtual bool operator==(const BalanceValueContainer& other) const;
|
|
|
|
virtual void init(void);
|
|
virtual void reset(void);
|
|
|
|
virtual bool load(void);
|
|
virtual bool save(void);
|
|
|
|
virtual const float get(const string& name, const float default_value) const;
|
|
virtual const int get(const string& name, const int default_value) const;
|
|
virtual const string get(const string& name, const string& default_value) const;
|
|
|
|
virtual const BalanceFloatCollection* getFloatMap(void) const;
|
|
virtual const BalanceIntCollection* getIntMap(void) const;
|
|
virtual const BalanceStringCollection* getStringMap(void) const;
|
|
|
|
virtual void insert(const string& name, const float value);
|
|
virtual void insert(const string& name, const int value);
|
|
virtual void insert(const string& name, const string& value);
|
|
|
|
virtual void remove(const string& name);
|
|
virtual void clear(void);
|
|
|
|
virtual void addListener(const BalanceChangeListener* listener);
|
|
virtual void addListener(const string& name, const BalanceChangeListener* listener);
|
|
virtual void removeListener(const BalanceChangeListener* listener);
|
|
virtual void removeListener(const string& name, const BalanceChangeListener* listener);
|
|
|
|
protected:
|
|
virtual BalanceListenerSet getListeners(const string& name, const BalanceValueType type);
|
|
virtual BalanceListenerSet getAllListeners(void);
|
|
virtual BalanceListenerSet getUniversalListeners(void);
|
|
|
|
BalanceFloatCollection float_values;
|
|
BalanceIntCollection int_values;
|
|
BalanceStringCollection string_values;
|
|
BalanceListenerIDSet universal_listeners;
|
|
map<string,BalanceListenerIDSet*> specific_field_listeners;
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// FileValueContainer - a value container that reads/writes from/to a text file
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
class FileValueContainer : public StandardValueContainer
|
|
{
|
|
public:
|
|
FileValueContainer(const string& filename);
|
|
virtual ~FileValueContainer(void);
|
|
|
|
virtual void init(void);
|
|
virtual void reset(void);
|
|
|
|
virtual bool load(void);
|
|
virtual bool save(void);
|
|
|
|
protected:
|
|
string m_Filename;
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// BalanceChangeListener implementation
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceChangeListener::BalanceChangeListener(void) : BCL_ID(g_BalanceSystem.getNextListenerID()) { g_BalanceSystem.registerListener(this); }
|
|
BalanceChangeListener::~BalanceChangeListener(void) { g_BalanceSystem.unregisterListener(this); }
|
|
unsigned int BalanceChangeListener::getBalanceChangeListenerID(void) const { return BCL_ID; }
|
|
bool BalanceChangeListener::shouldNotify(const string& name, const BalanceValueType type) const { return true; }
|
|
void BalanceChangeListener::balanceCleared(void) const {}
|
|
void BalanceChangeListener::balanceValueInserted(const string& name, const float value) const {}
|
|
void BalanceChangeListener::balanceValueInserted(const string& name, const int value) const {}
|
|
void BalanceChangeListener::balanceValueInserted(const string& name, const string& value) const {}
|
|
void BalanceChangeListener::balanceValueChanged(const string& name, const float old_value, const float new_value) const {}
|
|
void BalanceChangeListener::balanceValueChanged(const string& name, const int old_value, const int new_value) const {}
|
|
void BalanceChangeListener::balanceValueChanged(const string& name, const string& old_value, const string& default_value) const {}
|
|
void BalanceChangeListener::balanceValueRemoved(const string& name, const float old_value) const {}
|
|
void BalanceChangeListener::balanceValueRemoved(const string& name, const int old_value) const {}
|
|
void BalanceChangeListener::balanceValueRemoved(const string& name, const string& old_value) const {}
|
|
void BalanceChangeListener::balanceStartCompoundChange(void) const {}
|
|
void BalanceChangeListener::balanceEndCompoundChange(void) const {}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// BalanceValueContainer implementation
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceValueContainer::BalanceValueContainer(void) {}
|
|
BalanceValueContainer::~BalanceValueContainer(void) {}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// BalanceValueContainerFactory implementation
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceValueContainer* BalanceValueContainerFactory::get(void) { return g_BalanceSystem.getContainer(""); }
|
|
BalanceValueContainer* BalanceValueContainerFactory::get(const string& filename) { return g_BalanceSystem.getContainer(filename); }
|
|
const string& BalanceValueContainerFactory::getDefaultFilename(void) { return g_BalanceSystem.getDefaultFilename(); }
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// BalanceSystem implementation
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
extern const char* getModDirectory(void);
|
|
|
|
BalanceSystem::BalanceSystem(void) : nextID(0)
|
|
{
|
|
defaultFilename = getModDirectory();
|
|
defaultFilename += "/Balance.txt";
|
|
}
|
|
const string& BalanceSystem::getDefaultFilename(void) { return defaultFilename; }
|
|
unsigned int BalanceSystem::getNextListenerID(void) { return nextID++; }
|
|
void BalanceSystem::unregisterListener(const BalanceChangeListener* listener) { listeners.erase(listener->getBalanceChangeListenerID()); }
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void BalanceSystem::registerListener(const BalanceChangeListener* listener)
|
|
{
|
|
listeners.insert(map<unsigned int, const BalanceChangeListener*>::value_type(listener->getBalanceChangeListenerID(),listener));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceSystem::~BalanceSystem(void)
|
|
{
|
|
map<string,BalanceValueContainer*>::iterator current, end = containersByFilename.end();
|
|
for( current = containersByFilename.begin(); current != end; ++current )
|
|
{
|
|
delete current->second;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceSystem& BalanceSystem::getInstance(void)
|
|
{
|
|
static BalanceSystem system;
|
|
return system;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceValueContainer* BalanceSystem::getContainer(const string& filename)
|
|
{
|
|
BalanceValueContainer* returnVal = NULL;
|
|
#ifdef PLAYTEST_BUILD
|
|
map<string,BalanceValueContainer*>::iterator item = containersByFilename.find(filename);
|
|
if( item != containersByFilename.end() )
|
|
{
|
|
returnVal = item->second;
|
|
}
|
|
else if(filename.empty())
|
|
{
|
|
returnVal = new StandardValueContainer();
|
|
containersByFilename[filename] = returnVal;
|
|
}
|
|
else
|
|
{
|
|
returnVal = new FileValueContainer(filename);
|
|
containersByFilename[filename] = returnVal;
|
|
}
|
|
#else
|
|
map<string,BalanceValueContainer*>::iterator item = containersByFilename.find("");
|
|
if( item != containersByFilename.end() )
|
|
{
|
|
returnVal = item->second;
|
|
}
|
|
else
|
|
{
|
|
returnVal = new NullValueContainer();
|
|
containersByFilename[""] = returnVal;
|
|
}
|
|
#endif
|
|
|
|
return returnVal;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
const BalanceChangeListener* BalanceSystem::getListener(const unsigned int id)
|
|
{
|
|
const BalanceChangeListener* listener = NULL;
|
|
map<unsigned int, const BalanceChangeListener*>::iterator item = listeners.find(id);
|
|
if( item != listeners.end() )
|
|
{ listener = item->second; }
|
|
return listener;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// StandardBalanceVarCollection implementation
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
StandardValueContainer::StandardValueContainer(void) { init(); }
|
|
StandardValueContainer::~StandardValueContainer(void)
|
|
{
|
|
map<string,BalanceListenerIDSet*>::iterator current, end = specific_field_listeners.end();
|
|
for(current = specific_field_listeners.begin(); current != end; ++current)
|
|
{
|
|
delete current->second;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::init(void) {}
|
|
void StandardValueContainer::reset(void) { clear(); }
|
|
bool StandardValueContainer::load(void) { return false; }
|
|
bool StandardValueContainer::save(void) { return false; }
|
|
const BalanceFloatCollection* StandardValueContainer::getFloatMap(void) const { return &float_values; }
|
|
const BalanceIntCollection* StandardValueContainer::getIntMap(void) const { return &int_values; }
|
|
const BalanceStringCollection* StandardValueContainer::getStringMap(void) const { return &string_values; }
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceValueContainer& StandardValueContainer::operator=(const BalanceValueContainer& other)
|
|
{
|
|
if(&other == this) //shortcut if we're self-assigned
|
|
{ return *this; }
|
|
|
|
BalanceListenerSet listeners = getUniversalListeners();
|
|
|
|
{ //START COMPOUND OPERATION
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceStartCompoundChange();
|
|
}
|
|
}
|
|
|
|
{ //FLOAT VALUES
|
|
const BalanceFloatCollection* other_collection = other.getFloatMap();
|
|
BalanceFloatCollection::const_iterator current = float_values.begin(), end = float_values.end();
|
|
BalanceFloatCollection::const_iterator other_current = other_collection->begin(), other_end = other_collection->end();
|
|
vector<string> names_to_insert, names_to_remove;
|
|
while( current != end && other_current != other_end )
|
|
{
|
|
if( current->first < other_current->first )
|
|
{
|
|
names_to_remove.push_back(current->first);
|
|
++current;
|
|
continue;
|
|
}
|
|
if( other_current->first < current->first )
|
|
{
|
|
names_to_insert.push_back(other_current->first);
|
|
++other_current;
|
|
continue;
|
|
}
|
|
if( current->second != other_current->second )
|
|
{
|
|
names_to_insert.push_back(current->first);
|
|
}
|
|
++current;
|
|
++other_current;
|
|
}
|
|
while( current != end )
|
|
{
|
|
names_to_remove.push_back(current->first);
|
|
++current;
|
|
}
|
|
while( other_current != other_end )
|
|
{
|
|
names_to_insert.push_back(other_current->first);
|
|
++other_current;
|
|
}
|
|
vector<string>::iterator index, last = names_to_remove.end();
|
|
for( index = names_to_remove.begin(); index != last; ++index )
|
|
{
|
|
remove(*index);
|
|
}
|
|
last = names_to_insert.end();
|
|
for( index = names_to_insert.begin(); index != last; ++index )
|
|
{
|
|
insert(*index,other.get(*index,0.0f));
|
|
}
|
|
}
|
|
|
|
{ //INTEGER VALUES
|
|
const BalanceIntCollection* other_collection = other.getIntMap();
|
|
BalanceIntCollection::const_iterator current = int_values.begin(), end = int_values.end();
|
|
BalanceIntCollection::const_iterator other_current = other_collection->begin(), other_end = other_collection->end();
|
|
vector<string> names_to_insert, names_to_remove;
|
|
while( current != end && other_current != other_end )
|
|
{
|
|
if( current->first < other_current->first )
|
|
{
|
|
names_to_remove.push_back(current->first);
|
|
++current;
|
|
continue;
|
|
}
|
|
if( other_current->first < current->first )
|
|
{
|
|
names_to_insert.push_back(other_current->first);
|
|
++other_current;
|
|
continue;
|
|
}
|
|
if( current->second != other_current->second )
|
|
{
|
|
names_to_insert.push_back(current->first);
|
|
}
|
|
++current;
|
|
++other_current;
|
|
}
|
|
while( current != end )
|
|
{
|
|
names_to_remove.push_back(current->first);
|
|
++current;
|
|
}
|
|
while( other_current != other_end )
|
|
{
|
|
names_to_insert.push_back(other_current->first);
|
|
++other_current;
|
|
}
|
|
vector<string>::iterator index, last = names_to_remove.end();
|
|
for( index = names_to_remove.begin(); index != last; ++index )
|
|
{
|
|
remove(*index);
|
|
}
|
|
last = names_to_insert.end();
|
|
for( index = names_to_insert.begin(); index != last; ++index )
|
|
{
|
|
insert(*index,other.get(*index,0));
|
|
}
|
|
}
|
|
|
|
{ //STRING VALUES
|
|
const BalanceStringCollection* other_collection = other.getStringMap();
|
|
BalanceStringCollection::const_iterator current = string_values.begin(), end = string_values.end();
|
|
BalanceStringCollection::const_iterator other_current = other_collection->begin(), other_end = other_collection->end();
|
|
vector<string> names_to_insert, names_to_remove;
|
|
while( current != end && other_current != other_end )
|
|
{
|
|
if( current->first < other_current->first )
|
|
{
|
|
names_to_remove.push_back(current->first);
|
|
++current;
|
|
continue;
|
|
}
|
|
if( other_current->first < current->first )
|
|
{
|
|
names_to_insert.push_back(other_current->first);
|
|
++other_current;
|
|
continue;
|
|
}
|
|
if( current->second != other_current->second )
|
|
{
|
|
names_to_insert.push_back(current->first);
|
|
}
|
|
++current;
|
|
++other_current;
|
|
}
|
|
while( current != end )
|
|
{
|
|
names_to_remove.push_back(current->first);
|
|
++current;
|
|
}
|
|
while( other_current != other_end )
|
|
{
|
|
names_to_insert.push_back(other_current->first);
|
|
++other_current;
|
|
}
|
|
vector<string>::iterator index, last = names_to_remove.end();
|
|
for( index = names_to_remove.begin(); index != last; ++index )
|
|
{
|
|
remove(*index);
|
|
}
|
|
last = names_to_insert.end();
|
|
for( index = names_to_insert.begin(); index != last; ++index )
|
|
{
|
|
insert(*index,other.get(*index,""));
|
|
}
|
|
}
|
|
|
|
{ //END COMPOUND OPERATION
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceEndCompoundChange();
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
bool StandardValueContainer::operator!=(const BalanceValueContainer& other) const
|
|
{
|
|
return !operator==(other);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
bool StandardValueContainer::operator==(const BalanceValueContainer& other) const
|
|
{
|
|
return float_values == *other.getFloatMap()
|
|
&& int_values == *other.getIntMap()
|
|
&& string_values == *other.getStringMap();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::insert(const string& name, const int value)
|
|
{
|
|
BalanceListenerSet listeners = getListeners(name,BALANCE_TYPE_INT);
|
|
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceIntCollection::iterator item = int_values.find(name);
|
|
if( item != int_values.end() )
|
|
{
|
|
if( item->second == value )
|
|
{ return; }
|
|
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueChanged(name,item->second,value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueInserted(name,value);
|
|
}
|
|
}
|
|
}
|
|
int_values[name] = value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::insert(const string& name, const float value)
|
|
{
|
|
BalanceListenerSet listeners = getListeners(name,BALANCE_TYPE_FLOAT);
|
|
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceFloatCollection::iterator item = float_values.find(name);
|
|
if( item != float_values.end() )
|
|
{
|
|
if( item->second == value )
|
|
{ return; }
|
|
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueChanged(name,item->second,value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueInserted(name,value);
|
|
}
|
|
}
|
|
}
|
|
float_values[name] = value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::insert(const string& name, const string& value)
|
|
{
|
|
BalanceListenerSet listeners = getListeners(name,BALANCE_TYPE_STRING);
|
|
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceStringCollection::iterator item = string_values.find(name);
|
|
if( item != string_values.end() )
|
|
{
|
|
if( item->second == value )
|
|
{ return; }
|
|
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueChanged(name,item->second,value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueInserted(name,value);
|
|
}
|
|
}
|
|
}
|
|
string_values[name] = value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::remove(const string& name)
|
|
{
|
|
BalanceIntCollection::iterator item = int_values.find(name);
|
|
if( item != int_values.end() )
|
|
{
|
|
BalanceListenerSet listeners = getListeners(name,BALANCE_TYPE_INT);
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueRemoved(name,item->second);
|
|
}
|
|
}
|
|
int_values.erase(item);
|
|
}
|
|
|
|
BalanceFloatCollection::iterator fitem = float_values.find(name);
|
|
if( fitem != float_values.end() )
|
|
{
|
|
BalanceListenerSet listeners = getListeners(name,BALANCE_TYPE_FLOAT);
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueRemoved(name,fitem->second);
|
|
}
|
|
}
|
|
float_values.erase(fitem);
|
|
}
|
|
|
|
BalanceStringCollection::iterator sitem = string_values.find(name);
|
|
if( sitem != string_values.end() )
|
|
{
|
|
BalanceListenerSet listeners = getListeners(name,BALANCE_TYPE_STRING);
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceValueRemoved(name,sitem->second);
|
|
}
|
|
}
|
|
string_values.erase(sitem);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::clear(void)
|
|
{
|
|
BalanceListenerSet listeners = getAllListeners();
|
|
if( !listeners.empty() )
|
|
{
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceCleared();
|
|
}
|
|
}
|
|
|
|
int_values.clear();
|
|
float_values.clear();
|
|
string_values.clear();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
const int StandardValueContainer::get(const string& name, const int default_value) const
|
|
{
|
|
int value = default_value;
|
|
BalanceIntCollection::const_iterator item = int_values.find(name);
|
|
if( item != int_values.end() )
|
|
{ value = item->second; }
|
|
return value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
const float StandardValueContainer::get(const string& name, const float default_value) const
|
|
{
|
|
float value = default_value;
|
|
BalanceFloatCollection::const_iterator item = float_values.find(name);
|
|
if( item != float_values.end() )
|
|
{ value = item->second; }
|
|
return value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
const string StandardValueContainer::get(const string& name, const string& default_value) const
|
|
{
|
|
string value = default_value;
|
|
BalanceStringCollection::const_iterator item = string_values.find(name);
|
|
if( item != string_values.end() )
|
|
{ value = item->second; }
|
|
return value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::addListener(const BalanceChangeListener* listener)
|
|
{
|
|
universal_listeners.insert(listener->getBalanceChangeListenerID());
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::addListener(const string& name, const BalanceChangeListener* listener)
|
|
{
|
|
BalanceListenerIDSet* chain = NULL;
|
|
map<string,BalanceListenerIDSet*>::iterator item = specific_field_listeners.find(name);
|
|
if( item == specific_field_listeners.end() )
|
|
{
|
|
chain = new BalanceListenerIDSet();
|
|
specific_field_listeners[name] = chain;
|
|
}
|
|
else
|
|
{
|
|
chain = item->second;
|
|
}
|
|
chain->insert(listener->getBalanceChangeListenerID());
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::removeListener(const BalanceChangeListener* listener)
|
|
{
|
|
universal_listeners.erase(listener->getBalanceChangeListenerID());
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
void StandardValueContainer::removeListener(const string& name, const BalanceChangeListener* listener)
|
|
{
|
|
map<string,BalanceListenerIDSet*>::iterator item = specific_field_listeners.find(name);
|
|
if( item != specific_field_listeners.end() )
|
|
{
|
|
item->second->erase(listener->getBalanceChangeListenerID());
|
|
if(item->second->empty())
|
|
{
|
|
delete item->second;
|
|
specific_field_listeners.erase(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceListenerSet StandardValueContainer::getListeners(const string& name, const BalanceValueType type)
|
|
{
|
|
BalanceListenerSet chain;
|
|
const BalanceChangeListener* listener;
|
|
|
|
if( !specific_field_listeners.empty() )
|
|
{
|
|
map<string,BalanceListenerIDSet*>::iterator item = specific_field_listeners.find(name);
|
|
if( item != specific_field_listeners.end() )
|
|
{
|
|
BalanceListenerIDSet::iterator litem, current = item->second->begin(), end = item->second->end();
|
|
while( current != end )
|
|
{
|
|
listener = g_BalanceSystem.getListener(*current);
|
|
if( !listener )
|
|
{
|
|
litem = current;
|
|
++current;
|
|
item->second->erase(litem);
|
|
continue;
|
|
}
|
|
|
|
chain.insert(listener);
|
|
++current;
|
|
}
|
|
if( item->second->empty() )
|
|
{
|
|
delete item->second;
|
|
specific_field_listeners.erase(item);
|
|
}
|
|
}
|
|
}
|
|
if( !universal_listeners.empty() )
|
|
{
|
|
BalanceListenerIDSet::iterator item, current = universal_listeners.begin(), end = universal_listeners.end();
|
|
while( current != end )
|
|
{
|
|
listener = g_BalanceSystem.getListener(*current);
|
|
if( !listener )
|
|
{
|
|
item = current;
|
|
++current;
|
|
universal_listeners.erase(item);
|
|
continue;
|
|
}
|
|
if( listener->shouldNotify(name,type) )
|
|
{
|
|
chain.insert(listener);
|
|
}
|
|
++current;
|
|
}
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceListenerSet StandardValueContainer::getUniversalListeners(void)
|
|
{
|
|
BalanceListenerSet chain;
|
|
const BalanceChangeListener* listener;
|
|
|
|
if( !universal_listeners.empty() )
|
|
{
|
|
BalanceListenerIDSet::iterator item, current = universal_listeners.begin(), end = universal_listeners.end();
|
|
while( current != end )
|
|
{
|
|
listener = g_BalanceSystem.getListener(*current);
|
|
if( !listener )
|
|
{
|
|
item = current;
|
|
++current;
|
|
universal_listeners.erase(item);
|
|
continue;
|
|
}
|
|
chain.insert(listener);
|
|
++current;
|
|
}
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
BalanceListenerSet StandardValueContainer::getAllListeners(void)
|
|
{
|
|
BalanceListenerSet chain = getUniversalListeners();
|
|
const BalanceChangeListener* listener;
|
|
|
|
if( !specific_field_listeners.empty() )
|
|
{
|
|
map<string,BalanceListenerIDSet*>::iterator item, current = specific_field_listeners.begin();
|
|
map<string,BalanceListenerIDSet*>::iterator end = specific_field_listeners.end();
|
|
BalanceListenerIDSet::iterator item_id, current_id, end_id;
|
|
while( current != end )
|
|
{
|
|
current_id = current->second->begin();
|
|
end_id = current->second->end();
|
|
while( current_id != end_id )
|
|
{
|
|
listener = g_BalanceSystem.getListener(*current_id);
|
|
if( !listener )
|
|
{
|
|
item_id = current_id;
|
|
++current_id;
|
|
current->second->erase(item_id);
|
|
continue;
|
|
}
|
|
chain.insert(listener);
|
|
++current_id;
|
|
}
|
|
if( current->second->empty() )
|
|
{
|
|
item = current;
|
|
++current;
|
|
delete item->second;
|
|
specific_field_listeners.erase(item);
|
|
continue;
|
|
}
|
|
++current;
|
|
}
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// FileBalanceVarCollection implementation
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
FileValueContainer::FileValueContainer(const string& filename) : m_Filename(filename) {}
|
|
FileValueContainer::~FileValueContainer(void) {}
|
|
|
|
void FileValueContainer::init(void) { load(); }
|
|
void FileValueContainer::reset(void) { load(); }
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//CONSIDER: output bad lines to error
|
|
bool FileValueContainer::load(void)
|
|
{
|
|
string content;
|
|
|
|
ifstream file;
|
|
file.open(m_Filename.c_str());
|
|
if( !file.is_open() )
|
|
{ return false; }
|
|
|
|
BalanceListenerSet listeners = getUniversalListeners();
|
|
|
|
{ //START COMPOUND OPERATION
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceStartCompoundChange();
|
|
}
|
|
}
|
|
|
|
clear();
|
|
|
|
const static int LINE_MAX_LENGTH = 1024;
|
|
char line_src[LINE_MAX_LENGTH];
|
|
string line;
|
|
string name, string_value;
|
|
float float_value;
|
|
int int_value;
|
|
size_t index, end_index, dot_index;
|
|
while( !file.eof() )
|
|
{
|
|
file.getline(line_src,LINE_MAX_LENGTH);
|
|
|
|
//ignore comments and garbage
|
|
if( strncmp(line_src,"#define",7) ) { continue; }
|
|
line = string(&line_src[7]);
|
|
|
|
//get the name start
|
|
index = line.find_first_not_of(" \t",7);
|
|
if( index == string::npos ) { continue; }
|
|
|
|
//get the name end (whitepsace before value)
|
|
end_index = line.find_first_of(" \t",index);
|
|
if( end_index == string::npos ) { continue; }
|
|
|
|
name = line.substr(index,end_index);
|
|
|
|
//find beginning of the value
|
|
index = line.find_first_not_of(" \t",end_index);
|
|
if( index == string::npos ) { continue; }
|
|
|
|
//trim the end of the line
|
|
end_index = line.find_last_of(" \t");
|
|
if( end_index == string::npos || end_index < line.find_last_not_of(" \t") ) //cover cases like "this is a string with whitespace"\n
|
|
{ end_index = line.length(); }
|
|
|
|
//detect type and insert value
|
|
switch(line.at(end_index-1))
|
|
{
|
|
case '"': // string values
|
|
//skip a string mismatch
|
|
if(line.at(index) != '\"' || index == end_index) { continue; }
|
|
string_value = line.substr(index+1,end_index-1);
|
|
insert(name,string_value);
|
|
break;
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9': //float or integer values
|
|
//detect and process integer values
|
|
dot_index = line.find('.',index);
|
|
if( dot_index == string::npos )
|
|
{
|
|
int_value = atoi(line.substr(index,end_index).c_str());
|
|
insert(name,int_value);
|
|
break;
|
|
}
|
|
case 'f': //float values
|
|
float_value = (float)atof(line.substr(index,end_index).c_str());
|
|
insert(name,float_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
|
|
{ //END COMPOUND OPERATION
|
|
BalanceListenerSet::iterator current, end = listeners.end();
|
|
for( current = listeners.begin(); current != end; ++current )
|
|
{
|
|
(*current)->balanceEndCompoundChange();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
bool FileValueContainer::save(void)
|
|
{
|
|
ofstream file;
|
|
file.open(m_Filename.c_str());
|
|
if( !file.is_open() )
|
|
{ return false; }
|
|
|
|
file << "// Balance.txt - automatically generated balance data" << std::endl;
|
|
if( !int_values.empty() )
|
|
{
|
|
file << std::endl << "// INTEGER VALUES" << std::endl;
|
|
|
|
BalanceIntCollection::const_iterator icurrent, iend = int_values.end();
|
|
for( icurrent = int_values.begin(); icurrent != iend; ++icurrent )
|
|
{
|
|
file << "#define " << icurrent->first << " " << icurrent->second << std::endl;
|
|
}
|
|
}
|
|
|
|
if( !float_values.empty() )
|
|
{
|
|
file << std::endl << "// FLOAT VALUES" << std::endl;
|
|
file.precision(3);
|
|
file << std::ios::fixed;
|
|
BalanceFloatCollection::const_iterator fcurrent, fend = float_values.end();
|
|
for( fcurrent = float_values.begin(); fcurrent != fend; ++fcurrent )
|
|
{
|
|
file << "#define " << fcurrent->first << " " << fcurrent->second << "f" << std::endl;
|
|
}
|
|
}
|
|
|
|
if( !string_values.empty() )
|
|
{
|
|
file << std::endl << "// STRING VALUES" << std::endl;
|
|
BalanceStringCollection::const_iterator scurrent, send = string_values.end();
|
|
for( scurrent = string_values.begin(); scurrent != send; ++scurrent )
|
|
{
|
|
file << "#define " << scurrent->first << " \"" << scurrent->second << "\"" << std::endl;
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
return true;
|
|
}
|