Skip to content

Commit

Permalink
S/R Editor: Change the internal data structure from map to list, to a…
Browse files Browse the repository at this point in the history
…llow for convenient index changes. Refactor the rest of the code.

This resolves issue #5193, as S/R indices keep forming a proper sequence without any gaps after removal.
  • Loading branch information
codereader committed Mar 27, 2020
1 parent 41c4134 commit f77c78d
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 83 deletions.
96 changes: 68 additions & 28 deletions plugins/dm.stimresponse/SREntity.cpp
Expand Up @@ -34,9 +34,24 @@ int SREntity::getHighestIndex()

for (const auto& i : _list)
{
if (i.second.getIndex() > index)
if (i.getIndex() > index)
{
index = i.second.getIndex();
index = i.getIndex();
}
}

return index;
}

int SREntity::getHighestInheritedIndex()
{
int index = 0;

for (const auto& i : _list)
{
if (i.inherited() && i.getIndex() > index)
{
index = i.getIndex();
}
}

Expand All @@ -62,7 +77,7 @@ void SREntity::load(Entity* source)
// Instantiate a visitor class with the list of possible keys
// and the target list where all the S/Rs are stored
// Warning messages are stored in the <_warnings> string
SRPropertyLoader visitor(_keys, _list, _warnings);
SRPropertyLoader visitor(_keys, *this, _warnings);
eclass->forEachClassAttribute(std::ref(visitor));

// Scan the entity itself after the class has been searched
Expand All @@ -77,36 +92,48 @@ void SREntity::load(Entity* source)

void SREntity::remove(int index)
{
auto found = _list.find(index);
auto found = findByIndex(index);

if (found == _list.end() || found->second.inherited())
if (found == _list.end() || found->inherited())
{
return;
}

_list.erase(found);

// Re-arrange the S/R indices. The parser in the game engine
// Re-arrange the S/R indices. Otherwise he parser in the game engine
// walks up the indices and stops on the first missing index
// not noticing there might be higher indices to follow (#5193)
// TODO
int highestInheritedIndex = getHighestInheritedIndex();
int nextIndex = highestInheritedIndex + 1;

// Iterate over all non-inherited objects and assign new indices
for (auto i = _list.begin(); i != _list.end(); ++i)
{
if (!i->inherited())
{
// Found a non-inherited stim, assign the index
i->setIndex(nextIndex++);
}
}

updateListStores();
}

int SREntity::duplicate(int fromIndex)
{
auto found = _list.find(fromIndex);
auto found = findByIndex(fromIndex);

if (found != _list.end())
{
int index = getHighestIndex() + 1;

// Copy the object to the new id
_list[index] = found->second;
_list.push_back(StimResponse(*found));

// Set the index and the inheritance status
_list[index].setInherited(false);
_list[index].setIndex(index);
_list.back().setInherited(false);
_list.back().setIndex(index);

// Rebuild the liststores
updateListStores();
Expand All @@ -124,16 +151,12 @@ void SREntity::updateListStores()
_responseStore->Clear();

// Now populate the liststore
for (auto& i : _list)
for (auto& sr : _list)
{
int index = i.first;
StimResponse& sr = i.second;

wxutil::TreeModel::Row row = (sr.get("class") == "S") ?
_stimStore->AddItem() : _responseStore->AddItem();
auto row = sr.get("class") == "S" ? _stimStore->AddItem() : _responseStore->AddItem();

// Store the ID into the liststore
row[getColumns().index] = index;
// Store the index into the liststore
row[getColumns().index] = sr.getIndex();

writeToListRow(row, sr);

Expand All @@ -143,17 +166,20 @@ void SREntity::updateListStores()

int SREntity::add()
{
int index = getHighestIndex() + 1;
return add(getHighestIndex() + 1).getIndex();
}

StimResponse& SREntity::add(int index)
{
// Create a new StimResponse object
_list[index] = StimResponse();
_list.push_back(StimResponse());

// Set the index and the inheritance status
_list[index].setInherited(false);
_list[index].setIndex(index);
_list[index].set("class", "S");
_list.back().setInherited(false);
_list.back().setIndex(index);
_list.back().set("class", "S");

return index;
return _list.back();
}

void SREntity::cleanEntity(Entity* target)
Expand Down Expand Up @@ -184,7 +210,7 @@ void SREntity::save(Entity* target)

for (auto& i : _list)
{
saver.visit(i.second);
saver.visit(i);
}
}

Expand Down Expand Up @@ -245,9 +271,9 @@ void SREntity::setProperty(int index, const std::string& key, const std::string&

StimResponse& SREntity::get(int index)
{
auto i = _list.find(index);
auto i = findByIndex(index);

return i != _list.end() ? i->second : _emptyStimResponse;
return i != _list.end() ? *i : _emptyStimResponse;
}

const SRListColumns& SREntity::getColumns()
Expand Down Expand Up @@ -282,3 +308,17 @@ void SREntity::loadKeys()
_keys.push_back(newKey);
}
}

SREntity::StimsAndResponses::iterator SREntity::findByIndex(int index)
{
for (StimsAndResponses::iterator i = _list.begin(); i != _list.end(); ++i)
{
if (i->getIndex() == index)
{
return i;
}
}

return _list.end();
}

16 changes: 14 additions & 2 deletions plugins/dm.stimresponse/SREntity.h
Expand Up @@ -57,11 +57,11 @@ class SREntity
typedef std::vector<SRKey> KeyList;

// These are the int-indexed Stims/Responses belonging to an entity
typedef std::map<int, StimResponse> StimResponseMap;
typedef std::list<StimResponse> StimsAndResponses;

private:
// The local lists of S/R and possible keys
StimResponseMap _list;
StimsAndResponses _list;
KeyList _keys;

// The liststore representation
Expand Down Expand Up @@ -102,6 +102,12 @@ class SREntity
*/
int add();

/** greebo: Adds a new StimResponse and returns the index of the new object.
* The ListStore is NOT updated with this call to allow setting of
* the properties before refreshing the treeview.
*/
StimResponse& add(int index);

/** greebo: Removes the StimResponse object with the given index.
* This triggers a refresh of the liststores.
*/
Expand Down Expand Up @@ -147,7 +153,10 @@ class SREntity
*/
wxDataViewItem getIterForIndex(wxutil::TreeModel& targetStore, int index);

StimsAndResponses::iterator findByIndex(int index);

private:

/** greebo: Write the values of the passed StimResponse to the
* TreeModel using the passed Row.
* The index stays untouched.
Expand All @@ -159,5 +168,8 @@ class SREntity

// Returns the highest Stim/Response index number
int getHighestIndex();

// Returns the highest number used by inherited S/R, or 0 if no inherited S/R are present
int getHighestInheritedIndex();
};
typedef std::shared_ptr<SREntity> SREntityPtr;
94 changes: 45 additions & 49 deletions plugins/dm.stimresponse/SRPropertyLoader.cpp
Expand Up @@ -6,15 +6,12 @@
#include "gamelib.h"
#include <regex>

// Constructor
SRPropertyLoader::SRPropertyLoader(
SREntity::KeyList& keys,
SREntity::StimResponseMap& srMap,
std::string& warnings
) :
SRPropertyLoader::SRPropertyLoader(SREntity::KeyList& keys, SREntity& srEntity, std::string& warnings) :
_keys(keys),
_srMap(srMap),
_warnings(warnings)
_srEntity(srEntity),
_warnings(warnings),
_prefix(game::current::getValue<std::string>(GKEY_STIM_RESPONSE_PREFIX)),
_responseEffectPrefix(game::current::getValue<std::string>(GKEY_RESPONSE_EFFECT_PREFIX))
{}

void SRPropertyLoader::visitKeyValue(const std::string& key, const std::string& value)
Expand All @@ -27,20 +24,30 @@ void SRPropertyLoader::operator() (const EntityClassAttribute& attribute)
parseAttribute(attribute.getName(), attribute.getValue(), true);
}

/** greebo: Private helper method that does the attribute analysis
*/
void SRPropertyLoader::parseAttribute(
const std::string& key,
const std::string& value,
bool inherited)
StimResponse& SRPropertyLoader::findOrCreate(int index, bool inherited)
{
std::string prefix = game::current::getValue<std::string>(GKEY_STIM_RESPONSE_PREFIX);
auto& found = _srEntity.get(index);

// Now cycle through the possible key names and see if we have a match
for (std::size_t i = 0; i < _keys.size(); i++) {
if (found.getIndex() == index)
{
return found;
}

// Create the S/R object in case we got an empty S/R back
// Insert a new SR object with the given index
auto& sr = _srEntity.add(index);
sr.setInherited(inherited);

return sr;
}

void SRPropertyLoader::parseAttribute(const std::string& key, const std::string& value, bool inherited)
{
// Cycle through the possible key names and see if we have a match
for (const auto& srKey : _keys)
{
// Construct a regex with the number as match variable
std::string exprStr = "^" + prefix + _keys[i].key + "_([0-9]+)$";
std::string exprStr = "^" + _prefix + srKey.key + "_([0-9]+)$";
std::regex expr(exprStr);
std::smatch matches;

Expand All @@ -49,72 +56,61 @@ void SRPropertyLoader::parseAttribute(
// Retrieve the S/R index number
int index = string::convert<int>(matches[1].str());

// Check if the S/R with this index already exists
SREntity::StimResponseMap::iterator found = _srMap.find(index);

// Create the S/R object, if it doesn't exist yet
if (found == _srMap.end()) {
// Insert a new SR object with the given index
_srMap[index] = StimResponse();
_srMap[index].setIndex(index);
_srMap[index].setInherited(inherited);
}
// Ensure the S/R index exists in the list
auto& sr = findOrCreate(index, inherited);

// Check if the property already exists
if (!_srMap[index].get(_keys[i].key).empty()) {
if (!sr.get(srKey.key).empty())
{
// already existing, add to the warnings
_warnings += "Warning on StimResponse #" + string::to_string(index) +
": property " + _keys[i].key + " defined more than once.\n";
": property " + srKey.key + " defined more than once.\n";
}

// Set the property value on the StimResponse object
_srMap[index].set(_keys[i].key, value, inherited);
sr.set(srKey.key, value, inherited);
}
}

// Check the key for a Response Effect definition
{
std::string responseEffectPrefix =
game::current::getValue<std::string>(GKEY_RESPONSE_EFFECT_PREFIX);

// This should search for something like "sr_effect_2_3_arg3"
// (with the optional postfix "_argN" or "_state")
std::string exprStr =
"^" + prefix + responseEffectPrefix + "([0-9]+)_([0-9]+)(_arg[0-9]+|_state)*$";
"^" + _prefix + _responseEffectPrefix + "([0-9]+)_([0-9]+)(_arg[0-9]+|_state)*$";
std::regex expr(exprStr);
std::smatch matches;

if (std::regex_match(key, matches, expr)) {
if (std::regex_match(key, matches, expr))
{
// The response index
int index = string::convert<int>(matches[1].str());
// The effect index
int effectIndex = string::convert<int>(matches[2].str());

// Find the Response for this index
SREntity::StimResponseMap::iterator found = _srMap.find(index);

if (found == _srMap.end()) {
// Insert a new SR object with the given index
_srMap[index] = StimResponse();
_srMap[index].setIndex(index);
_srMap[index].setInherited(inherited);
}
// Ensure the S/R index exists in the list
auto& sr = findOrCreate(index, inherited);

// Get the response effect (or create a new one)
ResponseEffect& effect = _srMap[index].getResponseEffect(effectIndex);
ResponseEffect& effect = sr.getResponseEffect(effectIndex);

std::string postfix = matches[3];
if (postfix.empty()) {

if (postfix.empty())
{
// No "_arg1" found, the value is the effect name definition
effect.setName(value, inherited);
}
else if (postfix == "_state") {
else if (postfix == "_state")
{
// This is a state variable
effect.setActive(value != "0", inherited);
}
else {
else
{
// Get the argument index from the tail
int argIndex = string::convert<int>(postfix.substr(4));

// Load the value into argument with the index <argIndex>
effect.setArgument(argIndex, value, inherited);
}
Expand Down

0 comments on commit f77c78d

Please sign in to comment.