// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include "nel/misc/path.h"
#include "fame.h"
#include "nel/misc/diff_tool.h"
#include "ryzom_entity_id.h"
//#include "../entities_game_service/egs_variables.h"
//#include "pvp_clan.h"
using namespace std;
using namespace NLMISC;
using namespace STRING_MANAGER;
std::string RowIdToStringCb(void *value);
std::string FameToStringCb(void *value)
{
sint32 &fame = *((sint32*)value);
if (fame == NO_FAME)
return "NO_FAME";
else
return toString("%.3f", fame/1000.0);
}
CStaticFames *CStaticFames::_Instance = NULL;
CFameInterface *CFameInterface::_Instance = NULL;
sint16 CFameInterface::TFameOwner::CivilisationPropIndex = 0;
sint16 CFameInterface::TFameOwner::GuildPropIndex = 0;
sint16 CFameInterface::TFameOwner::FameMemoryPropIndex = 0;
sint16 CFameInterface::TFameOwner::FirstFamePropIndex = 0;
CVariable UseAsymmetricStaticFames("variables", "UseAsymmetricStaticFames", "if 1, fame between faction A and B may be different that between B and A", false, 0, true);
//----------------------------------------------------------------------------
NLMISC_COMMAND(displayFactions, "Display a list of fame faction", "")
{
CStaticFames &sf = CStaticFames::getInstance();
const std::vector &names = sf.getFactionNames();
log.displayNL("Listing %u factions:", names.size());
for (uint i=0; i]")
{
if (args.size() > 1)
return false;
CStaticFames &sf = CStaticFames::getInstance();
const std::vector &names = sf.getFactionNames();
uint start =0;
uint end = names.size();
if (args.size() == 1)
{
uint32 factionIndex = sf.getFactionIndex(args[0]);
if (factionIndex != CStaticFames::INVALID_FACTION_INDEX)
{
start = factionIndex;
end = start+1;
}
else
{
log.displayNL("invalid faction name '%s'", args[0].c_str());
return true;
}
}
for (uint i=start; i [] []* ")
{
if (args.size() < 1)
return false;
CFameInterface &fi = CFameInterface::getInstance();
CEntityId eid(args[0]);
if (eid == CEntityId::Unknown)
{
log.display("Invalid entity id '%s'", args[0].c_str());
return false;
}
TDataSetRow dsr = fi.getFameDataSet()->getDataSetRow(eid);
if (!fi.getFameDataSet()->isAccessible(dsr))
{
log.display("Invalid entity id '%s', can't map in fame dataset", args[0].c_str());
return false;
}
const vector factionNames = CStaticFames::getInstance().getFactionNames();
// uint32 staticIndex = CStaticFames::INVALID_FACTION_INDEX;
bool detail = false;
if (args.size() >= 2)
{
if (args[1] == "detail")
{
detail = true;
}
}
// build the filter list
vector factionFilter;
for (uint i=(detail ? 2 : 1); igetEntityId(mdsr), i, false, true);
if (fame != NO_FAME)
fm = toString("%7.3f", fame/1000.0f);
else
fm = "no fame";
}
case RYZOMID::guild:
if (fi.getFameDataSet()->isAccessible(gdsr))
{
sint32 fame = fi.getFameIndexed(fi.getFameDataSet()->getEntityId(gdsr), i, false, true);
if (fame != NO_FAME)
fg = toString("%7.3f", fame/1000.0f);
else
fg = "no fame";
if (eid.getType() == RYZOMID::guild)
cdsr = fi.getCivilisationIndex(eid);
}
case RYZOMID::civilisation:
if (fi.getFameDataSet()->isAccessible(cdsr))
{
sint32 fame = fi.getFameIndexed(fi.getFameDataSet()->getEntityId(cdsr), i, false, true);
if (fame != NO_FAME)
fc = toString("%7.3f", fame/1000.0f);
else
fc = "no fame";
}
}
// display the result
log.displayNL("Fame between \t%s and %35s = P: %7s, G: %7s, M: %7s, C: %7s",
eid.toString().c_str(),
factionName.c_str(),
fp.c_str(),
fg.c_str(),
fm.c_str(),
fc.c_str());
}
}
}
return true;
}
//----------------------------------------------------------------------------
NLMISC_COMMAND(addFame, "Add some fame value between an entity and a faction.", " ")
{
if (args.size() != 3)
return false;
CEntityId eid(args[0]);
if (eid == CEntityId::Unknown)
{
log.display("Invalid entity id '%s'", args[0].c_str());
return false;
}
TDataSetRow dsr = CFameInterface::getInstance().getFameDataSet()->getDataSetRow(eid);
if (!CFameInterface::getInstance().getFameDataSet()->isAccessible(dsr))
{
log.display("Invalid entity id '%s', can't map in fame dataset", args[0].c_str());
return false;
}
uint32 factionIndex;
if (isdigit(args[1][0]))
{
fromString(args[1], factionIndex);
}
else
{
factionIndex = CStaticFames::getInstance().getFactionIndex(args[1]);
}
if (factionIndex == CStaticFames::INVALID_FACTION_INDEX)
{
log.display("Invalid faction '%s'.\nTry displayFactions to list the available factions.", args[1].c_str());
return false;
}
sint32 delta;
fromString(args[2], delta);
// clamp(delta, FameAbsoluteMin, FameAbsoluteMax);
// finaly, retreive the
CFameInterface::getInstance().addFameIndexed(eid, factionIndex, delta, true);
return true;
}
//----------------------------------------------------------------------------
CStaticFames::CStaticFames()
{
_FameTable = NULL;
_PropagationFactorTable = NULL;
loadStaticFame("static_fame.txt");
loadTribeThreshold("fame_tribes_threshold.txt");
}
//----------------------------------------------------------------------------
CStaticFames::~CStaticFames()
{
delete _FameTable;
_FameTable = NULL;
delete _PropagationFactorTable;
_PropagationFactorTable = NULL;
}
//----------------------------------------------------------------------------
void CStaticFames::releaseInstance()
{
if( _Instance )
{
delete _Instance;
_Instance = NULL;
}
}
//----------------------------------------------------------------------------
void CStaticFames::loadStaticFame( const string& filename )
{
_FameTableSize = 0;
_FirstTribeFameIndex = 0;
_FameTable = NULL;
string path = CPath::lookup(filename, false);
if (path.empty())
{
nlwarning("FAME: Error can't find static fame file '%s' ! No static fame data available.",
filename.c_str());
return;
}
STRING_MANAGER::TWorksheet ws;
// load the static fame file
if (loadExcelSheet(path, ws, false))
{
// ok, we can parse the worksheet
// 1st, build the index table
for (uint i=1; isecond != i-2)
{
nlwarning("FAME: Error while reading fame file '%s' : identifier '%s' is in row %u but in column %u, column and row index must match !",
path.c_str(),
name.c_str(),
it->second,
i-2
);
nlwarning("FAME: The faction data will not be available !");
_FactionNameIndex.clear();
_FactionNames.clear();
_FactionSheets.clear();
_FactionSheetIndex.clear();
return;
}
}
// 3rd, allocate fame table and load the data
_FameTableSize = _FactionNameIndex.size();
_FameTable = new sint32[_FameTableSize*_FameTableSize];
_PropagationFactorTable = new float[_FameTableSize*_FameTableSize];
_FameIndexToDatabaseIndex.resize( _FameTableSize );
// Parse database indices
for (uint i=1; i<_FameTableSize+1; ++i)
{
int iFaction = i-1;
string s = ws.getData(i, 1).toString();
// get rid of " characters
string::size_type k;
while ( (k = s.find_first_of('"')) != string::npos)
s.erase(k,1);
uint databaseIndex;
fromString(s, databaseIndex);
if( databaseIndex >= 1000 )
{
databaseIndex -= 1000;
_FirstTribeFameIndex = iFaction;
}
_FameIndexToDatabaseIndex[iFaction] = databaseIndex;
}
// Parse static fames and propagation factors
for (uint i=1; i<_FameTableSize+1; ++i)
{
int iFaction = i-1;
for (uint j=2; j<_FameTableSize+2; ++j)
{
int jFaction = j-2;
string s = ws.getData(i, j).toString();
// get rid of " characters
string::size_type k;
while ( (k = s.find_first_of('"')) != string::npos)
s.erase(k,1);
// Extract "fame;factor"
float factor = 0.0f;
string::size_type sep = s.find_first_of(';');
if (sep == string::npos)
sep = s.size();
else
factor = (float) atof( s.substr(sep+1, s.size()-sep-1).c_str());
// Fames in file are in [-600;600] so don't forget 1000 factor
sint32 fame = (sint32)(atof(s.substr(0,sep).c_str())*1000.f);
_FameTable[iFaction*_FameTableSize + jFaction] = fame;
_PropagationFactorTable[iFaction*_FameTableSize + jFaction] = factor;
if (fame != 0)
{
/* nldebug ("FAME: Setting fame between '%s' and '%s' to value %f, propagation factor %f",
CStringMapper::unmap(_FactionNames[iFaction]).c_str(),
CStringMapper::unmap(_FactionNames[jFaction]).c_str(),
fame/1000.0f,
factor
);*/
}
}
}
}
else
{
nlwarning("FAME: Error while reading the static fame file '%s' ! No static fame data available.",
path.c_str());
return;
}
nlassert(_FactionNames.size() == _FactionSheets.size() );
nlinfo("FAME: Loaded fame for %u factions, total of %u fame values",
_FameTableSize,
_FameTableSize*_FameTableSize);
}
//----------------------------------------------------------------------------
void CStaticFames::loadTribeThreshold( const string& filename )
{
string path = CPath::lookup(filename, false);
if (path.empty())
{
nlwarning("FAME: Error can't find tribe threshold fame file '%s' ! No tribe threshold available.", filename.c_str());
return;
}
STRING_MANAGER::TWorksheet ws;
// load the tribe threshold fame file
if (loadExcelSheet(path, ws, false))
{
// ok, we can parse the worksheet
// check table structure
uint nbTribe = ws.size()-2;
nlassert(nbTribe<=_FameTableSize);
nlassert(ws.ColCount == 16); // 5 ( 4 people + neutral ) * 3 cult + 1 for tribe name
_TribeCultThresholdPerCiv.resize(nbTribe);
for (uint i=2; isetKami(thresholdKami*6000);
tc->setKaravan(thresholdKaravan*6000);
tc->setNeutral(thresholdNeutral*6000);
// This message removed by Sadge because there is no context displayed, meaning that message must be useless
// nldebug(" %s", ws.getData(i, c).toString().c_str() );
}
}
}
}
//----------------------------------------------------------------------------
const std::string &CStaticFames::getFactionName(uint32 factionIndex)
{
const static std::string emptyString;
if (factionIndex < _FactionNames.size())
return NLMISC::CStringMapper::unmap(_FactionNames[factionIndex]);
else
return emptyString;
}
//----------------------------------------------------------------------------
uint CStaticFames::getFactionIndex(NLMISC::TStringId factionName)
{
TFactionNameIndexList::iterator it(_FactionNameIndex.find(factionName));
if (it != _FactionNameIndex.end())
{
return it->second;
}
return INVALID_FACTION_INDEX;
}
//----------------------------------------------------------------------------
uint CStaticFames::getFactionIndex(const std::string &factionName)
{
string n = strlwr(factionName);
return getFactionIndex(CStringMapper::map(n));
}
//----------------------------------------------------------------------------
sint32 CStaticFames::getStaticFameIndexed(uint factionIndex1, uint factionIndex2)
{
if ((factionIndex1 < factionIndex2) && !UseAsymmetricStaticFames)
swap(factionIndex1, factionIndex2);
if (factionIndex1 >= _FameTableSize || factionIndex2 >= _FameTableSize)
{
nlwarning("FAME: CStaticFames::getStaticFame invalid faction, return 0");
return 0;
}
#ifdef NL_DEBUG
nlassert(factionIndex1 < _FameTableSize);
nlassert(factionIndex2 < _FameTableSize);
#endif
return _FameTable[factionIndex1*_FameTableSize + factionIndex2];
}
//----------------------------------------------------------------------------
float CStaticFames::getPropagationFactorIndexed(uint factionIndex1, uint factionIndex2) const
{
if ((factionIndex1 < factionIndex2) && !UseAsymmetricStaticFames)
swap(factionIndex1, factionIndex2);
if (factionIndex1 >= _FameTableSize || factionIndex2 >= _FameTableSize)
{
nlwarning("FAME: CStaticFames::getPropagationFactorIndexed invalid faction, return 0");
return 0.0f;
}
#ifdef NL_DEBUG
nlassert(factionIndex1 < _FameTableSize);
nlassert(factionIndex2 < _FameTableSize);
#endif
return _PropagationFactorTable[factionIndex1*_FameTableSize + factionIndex2];
}
//----------------------------------------------------------------------------
sint32 CStaticFames::getStaticFame(NLMISC::TStringId faction1, NLMISC::TStringId faction2)
{
uint index1, index2;
// retreive fame index
index1 = getFactionIndex(faction1);
index2 = getFactionIndex(faction2);
return getStaticFameIndexed(index1, index2);
}
//----------------------------------------------------------------------------
sint32 CStaticFames::getStaticFame(const std::string &faction1, const std::string &faction2)
{
string n1, n2;
n1 = strlwr(faction1);
n2 = strlwr(faction2);
return getStaticFame(CStringMapper::map(n1), CStringMapper::map(n2));
}
//----------------------------------------------------------------------------
sint32 CFameInterface::getFameIndexed(const CEntityId &entityId, uint32 factionIndex, bool modulated, bool returnUnknownValue)
{
if (_FameOverload)
{
return _FameOverload->getFameIndexed(entityId, factionIndex, modulated, returnUnknownValue);
}
else
{
// if no faction, just return 0
if (factionIndex == CStaticFames::INVALID_FACTION_INDEX)
return 0;
// retrieve the faction
TDataSetRow entityRow = _FameDataSet->getDataSetRow(entityId);
TFameContainer::iterator it(_FamesOwners.find(entityRow));
if (it == _FamesOwners.end())
{
// nlwarning("FAME: entity %x doesn't exist in fame owners containers!", entityIndex.getIndex());
return 0;
}
if (factionIndex >= MAX_FACTION)
{
nlwarning("FAME: entity %s : faction %u is out of limit (%u)", entityId.toString().c_str(), factionIndex, MAX_FACTION);
return 0;
}
TFameOwner *fo = it->second;
sint32 fame = fo->Fames[factionIndex];
// The return values *should* be clamped here, but the values are stored in the
// EGS. Just return the raw value if we don't have the _FameOverload.
if (!returnUnknownValue && fame == NO_FAME)
fame = 0;
return sint32(fame);
}
}
//----------------------------------------------------------------------------
sint32 CFameInterface::getFactionIndex(TStringId faction)
{
CStaticFames &staticFame = CStaticFames::getInstance();
return staticFame.getFactionIndex(faction);
}
//----------------------------------------------------------------------------
sint32 CFameInterface::getFame(const CEntityId &entityId, TStringId faction, bool modulated, bool returnUnknowValue)
{
CStaticFames &staticFame = CStaticFames::getInstance();
// get the fame faction index
uint oi = staticFame.getFactionIndex(faction);
if(oi == CStaticFames::INVALID_FACTION_INDEX)
return NO_FAME;
return getFameIndexed(entityId, oi, modulated, returnUnknowValue);
}
//----------------------------------------------------------------------------
void CFameInterface::addFame(const CEntityId &entityId, NLMISC::TStringId faction, sint32 deltaFame, bool propagate)
{
// get the fame faction index
CStaticFames &staticFame = CStaticFames::getInstance();
uint32 oi = staticFame.getFactionIndex(faction);
if (oi == CStaticFames::INVALID_FACTION_INDEX)
{
nlwarning("FAME:addFame(): trying to add fame to faction '%s'(stringId=%u) which is not a valid faction name !",
CStringMapper::unmap(faction).c_str(),
faction);
return;
}
addFameIndexed(entityId, oi, deltaFame, propagate);
}
//----------------------------------------------------------------------------
void CFameInterface::addFameIndexed(const CEntityId &entityId, uint32 factionIndex, sint32 deltaFame, bool propagate)
{
if (_FameOverload)
{
_FameOverload->addFameIndexed(entityId, factionIndex, deltaFame, propagate);
}
else
{
// retrieve the fame owner.
TDataSetRow dsr = _FameDataSet->getDataSetRow(entityId);
TFameContainer::iterator it(_FamesOwners.find(dsr));
if (it == _FamesOwners.end())
{
nlwarning("FAME:addFameIndexed(): can't find info for entity %s(index=%u) in _FamesOwners, can't add fame",
entityId.toString().c_str(),
dsr.toString().c_str());
return;
}
if (factionIndex >= MAX_FACTION)
{
nlwarning("FAME:addFameIndexed(): trying to add fame for entity %s to an invalid faction %u, can't add fame",
entityId.toString().c_str(),
factionIndex);
return;
}
NLNET::CMessage msg("FAME_DELTA");
// the entity index
msg.serial(const_cast(entityId));
// the other faction index
msg.serial(factionIndex);
// the delta value
msg.serial(deltaFame);
msg.serial(propagate);
// send the message
NLNET::CUnifiedNetwork::getInstance()->send("EGS", msg);
}
}
//----------------------------------------------------------------------------
const TDataSetRow &CFameInterface::getCivilisationIndex(const CEntityId &entityId)
{
static const TDataSetRow invalidIndex;
if (_FameOverload)
{
return _FameOverload->getCivilisationIndex(entityId);
}
else
{
TDataSetRow dsr = _FameDataSet->getDataSetRow(entityId);
TFameContainer::iterator it(_FamesOwners.find(dsr));
if (it == _FamesOwners.end())
return invalidIndex;
return it->second->Civilisation;
}
}
//----------------------------------------------------------------------------
const TDataSetRow &CFameInterface::getGuildIndex(const CEntityId &entityId)
{
static const TDataSetRow invalidIndex;
if (_FameOverload)
{
return _FameOverload->getGuildIndex(entityId);
}
else
{
TDataSetRow dsr = _FameDataSet->getDataSetRow(entityId);
TFameContainer::iterator it(_FamesOwners.find(dsr));
if (it == _FamesOwners.end())
return invalidIndex;
return it->second->Guild;
}
}
//----------------------------------------------------------------------------
const TDataSetRow &CFameInterface::getFameMemoryIndex(const CEntityId &entityId)
{
static const TDataSetRow invalidIndex;
if (_FameOverload)
{
return _FameOverload->getFameMemoryIndex(entityId);
}
else
{
TDataSetRow dsr = _FameDataSet->getDataSetRow(entityId);
TFameContainer::iterator it(_FamesOwners.find(dsr));
if (it == _FamesOwners.end())
return invalidIndex;
return it->second->FameMemory;
}
}
//----------------------------------------------------------------------------
void CFameInterface::setFameDataSet(CMirroredDataSet *fameDataSet, bool declareProperty)
{
const static std::string civilisation("Civilisation");
const static std::string guild("Guild");
const static std::string fameMemory("FameMemory");
const static std::string firstFame("Fame_0");
_FameDataSet = fameDataSet;
if (!_FameDataSet)
return;
if (declareProperty)
{
// declare the fame properties
fameDataSet->declareProperty( civilisation, PSOReadOnly );
fameDataSet->declareProperty( fameMemory, PSOReadOnly);
fameDataSet->declareProperty( guild, PSOReadOnly);
for (uint i=0; ideclareProperty( propName, PSOReadOnly );
}
}
// initialise property index
TFameOwner::CivilisationPropIndex = _FameDataSet->getPropertyIndex("Civilisation");
TFameOwner::GuildPropIndex = _FameDataSet->getPropertyIndex("Guild");
TFameOwner::FameMemoryPropIndex = _FameDataSet->getPropertyIndex("FameMemory");
TFameOwner::FirstFamePropIndex = _FameDataSet->getPropertyIndex("Fame_0");
// set display call backs
_FameDataSet->setDisplayCallback( TFameOwner::CivilisationPropIndex, RowIdToStringCb );
_FameDataSet->setDisplayCallback( TFameOwner::GuildPropIndex, RowIdToStringCb );
_FameDataSet->setDisplayCallback( TFameOwner::FameMemoryPropIndex, RowIdToStringCb );
for (uint i=0; isetDisplayCallback( TFameOwner::FirstFamePropIndex+i, FameToStringCb );
}