// 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 "bar_manager.h"
#include "interface_manager.h"
#include "interface_expr.h"
#include "../time_client.h"
using namespace std;
using namespace NLMISC;
// ***************************************************************************
// LOG, for debug
#define ALLOW_BAR_LOG 0
// don't change this (ensure no bug in final version)
#if FINAL_VERSION
#undef ALLOW_BAR_LOG
#define ALLOW_BAR_LOG 0
#endif
#if ALLOW_BAR_LOG
# define barInfoLog nlinfo
#else // NL_RELEASE
# ifdef NL_COMP_VC71
# define barInfoLog __noop
# else
# define barInfoLog 0&&
# endif
#endif // NL_RELEASE
static const char *entryTypeToStr(CBarManager::TEntryType et)
{
switch(et)
{
case CBarManager::EntityType: return "Entity"; break;
case CBarManager::TeamMemberType: return "TeamMember"; break;
case CBarManager::AnimalType: return "Animal"; break;
case CBarManager::TargetType: return "Target"; break;
default: return "Unknown (Error!!)"; break;
}
}
// ***************************************************************************
CBarManager *CBarManager::_Instance= NULL;
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
CBarManager::CBarDataEntry::CBarDataEntry()
{
clear();
resetDB();
}
// ***************************************************************************
void CBarManager::CBarDataEntry::clear()
{
DataSetId= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX;
BarInfo.reset();
}
// ***************************************************************************
void CBarManager::CBarDataEntry::resetDB()
{
UIDIn= NULL;
PresentIn= NULL;
for(uint sc=0;scgetDbProp(baseDBin+"UID", false);
if(!presentDB.empty())
PresentIn= pIM->getDbProp(baseDBin+presentDB, false);
if(!hpDB.empty())
ScoreIn[SCORES::hit_points]= pIM->getDbProp(baseDBin+hpDB, false);
if(!sapDB.empty())
ScoreIn[SCORES::sap]= pIM->getDbProp(baseDBin+sapDB, false);
if(!staDB.empty())
ScoreIn[SCORES::stamina]= pIM->getDbProp(baseDBin+staDB, false);
if(!focusDB.empty())
ScoreIn[SCORES::focus]= pIM->getDbProp(baseDBin+focusDB, false);
}
// try to connect each output entry (don't create)
if(!baseDBout.empty())
{
if(!hpDB.empty())
ScoreOut[SCORES::hit_points]= pIM->getDbProp(baseDBout+hpDB, false);
if(!sapDB.empty())
ScoreOut[SCORES::sap]= pIM->getDbProp(baseDBout+sapDB, false);
if(!staDB.empty())
ScoreOut[SCORES::stamina]= pIM->getDbProp(baseDBout+staDB, false);
if(!focusDB.empty())
ScoreOut[SCORES::focus]= pIM->getDbProp(baseDBout+focusDB, false);
}
}
// ***************************************************************************
void CBarManager::CBarDataEntry::flushDBOut()
{
for(uint sc=0;scsetValue8(BarInfo.Score[sc]);
}
}
// ***************************************************************************
void CBarManager::CBarDataEntry::modifyFromDBIn(CBarInfo &barInfo) const
{
for(uint sc=0;scgetValue8();
}
}
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
CBarManager::CBarManager()
{
// if more than 256 entities, must change MaxEntity
nlctassert(sizeof(CLFECOMMON::TCLEntityId)==1);
// Allocate the array now
nlctassert(MaxEntryType==4);
_EntryBars[EntityType].resize(MaxEntity);
_EntryBars[TeamMemberType].resize(MaxTeamMember);
_EntryBars[AnimalType].resize(MaxAnimal);
_EntryBars[TargetType].resize(MaxTarget);
// no msg still sent
_LastUserBarMsgNumber= 0;
}
// ***************************************************************************
void CBarManager::releaseInstance()
{
if( _Instance )
{
delete _Instance;
_Instance = NULL;
}
}
// ***************************************************************************
void CBarManager::initInGame()
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
//resetShardSpecificData(); // directly called by CFarTP::disconnectFromPreviousShard()
/* *** Verify that MaxTeamMember is correct according to database
change MaxTeamMember if no more the case
*/
uint i=0;
while( pIM->getDbProp(toString("SERVER:GROUP:%d:NAME", i), false) )
i++;
nlassert(i==MaxTeamMember);
// *** create connexion to the Local Output database
for(i=0;i<_EntryBars[TeamMemberType].size();i++)
{
// don't connect FOCUS, since not setuped by SERVER
_EntryBars[TeamMemberType][i].connectDB(
toString("SERVER:GROUP:%d:",i),
toString("UI:VARIABLES:BARS:TEAM:%d:",i),
"PRESENT",
"HP", "SAP", "STA", "");
}
for(i=0;i<_EntryBars[AnimalType].size();i++)
{
// don't connect STA, SAP and FOCUS for animal, since they don't have
_EntryBars[AnimalType][i].connectDB(
toString("SERVER:PACK_ANIMAL:BEAST%d:",i),
toString("UI:VARIABLES:BARS:ANIMAL:%d:",i),
"STATUS",
"HP", "", "", "");
}
nlassert(_EntryBars[TargetType].size()==1);
_EntryBars[TargetType][0].connectDB(
"SERVER:TARGET:BARS:",
"UI:VARIABLES:BARS:TARGET:",
"", // no present flag for target (not so important)
"HP", "SAP", "STA", "FOCUS");
// NB: don't connect the DB for entities, since CEntityCL read it directly from getBarsByEntityId() (simpler and faster)
// *** init _EntryScoreFlags
nlctassert(MaxEntryType==4);
nlctassert(SCORES::NUM_SCORES==4);
// For each entry type, tells what score they can affect (see DB connection above)
_EntryScoreFlags[EntityType]= HpFlag | SapFlag | StaFlag | FocusFlag; // all
_EntryScoreFlags[TeamMemberType]= HpFlag | SapFlag | StaFlag; // anything but focus
_EntryScoreFlags[AnimalType]= HpFlag; // Hp only
_EntryScoreFlags[TargetType]= HpFlag | SapFlag | StaFlag | FocusFlag; // all
// *** create connexion for User Bar mgt
// user now can only manage 4 scores
nlctassert(SCORES::NUM_SCORES==4);
// Input max values
_UserScores[SCORES::hit_points].DBInMax= pIM->getDbProp("SERVER:CHARACTER_INFO:SCORES0:Max", false);
_UserScores[SCORES::sap].DBInMax= pIM->getDbProp("SERVER:CHARACTER_INFO:SCORES2:Max", false);
_UserScores[SCORES::stamina].DBInMax= pIM->getDbProp("SERVER:CHARACTER_INFO:SCORES1:Max", false);
_UserScores[SCORES::focus].DBInMax= pIM->getDbProp("SERVER:CHARACTER_INFO:SCORES3:Max", false);
// Output real values
_UserScores[SCORES::hit_points].DBOutVal= pIM->getDbProp("UI:VARIABLES:USER:HP", false);
_UserScores[SCORES::sap].DBOutVal= pIM->getDbProp("UI:VARIABLES:USER:SAP", false);
_UserScores[SCORES::stamina].DBOutVal= pIM->getDbProp("UI:VARIABLES:USER:STA", false);
_UserScores[SCORES::focus].DBOutVal= pIM->getDbProp("UI:VARIABLES:USER:FOCUS", false);
// Output ratio values
_UserScores[SCORES::hit_points].DBOutRatio= pIM->getDbProp("UI:VARIABLES:USER:HP_RATIO", false);
_UserScores[SCORES::sap].DBOutRatio= pIM->getDbProp("UI:VARIABLES:USER:SAP_RATIO", false);
_UserScores[SCORES::stamina].DBOutRatio= pIM->getDbProp("UI:VARIABLES:USER:STA_RATIO", false);
_UserScores[SCORES::focus].DBOutRatio= pIM->getDbProp("UI:VARIABLES:USER:FOCUS_RATIO", false);
}
// ***************************************************************************
void CBarManager::releaseInGame()
{
// Reset all, and also the DB out
for(uint type=0;type &entryArray= _EntryBars[type];
nlassert(entryId &entryArray= _EntryBars[type];
nlassert(entryIdsecond;
for(uint sc=0;sc=barUid.ScoreDate[sc] )
{
// then change this score with the more recent one!
barUid.ScoreDate[sc]= serverTick;
barUid.BarInfo.Score[sc]= barInfo.Score[sc];
}
}
// and report result to all connected entries
for(uint type=0;type::iterator itEid= barUid.EntryId[type].begin();
std::set::iterator itEidEnd= barUid.EntryId[type].end();
for(;itEid!=itEidEnd;itEid++)
{
uint entryId= *itEid;
nlassert(entryId<_EntryBars[type].size());
CBarDataEntry &bde= _EntryBars[type][entryId];
// copy the bar info (with relevant values)
bde.BarInfo= barUid.BarInfo;
// and flush DB (if any)
bde.flushDBOut();
}
}
}
// ***************************************************************************
void CBarManager::updateEntryFromDBNoAddDel(TEntryType type, CBarDataEntry &bde)
{
// get the Bar Info, from the input DB of this entry (only values linked)
CBarInfo barInfo;
bde.modifyFromDBIn(barInfo);
// Get the last DB modification server tick
TGameCycle serverTick= 0;
/* To do this, I test all DB input, and choose the highest changeDate
This works because the branch is atomic (all DB are relevant to the others, and
therefore relevant agst the most recent one)
*/
if(bde.UIDIn && bde.UIDIn->getLastChangeGC() > serverTick )
serverTick= bde.UIDIn->getLastChangeGC();
if(bde.PresentIn && bde.PresentIn->getLastChangeGC() > serverTick )
serverTick= bde.PresentIn->getLastChangeGC();
for(uint sc=0;scgetLastChangeGC() > serverTick )
serverTick= bde.ScoreIn[sc]->getLastChangeGC();
}
// then update the bars
updateBars(bde.DataSetId, barInfo, serverTick, _EntryScoreFlags[type] );
}
// ***************************************************************************
void CBarManager::updateEntryFromDB(TEntryType type, uint entryId)
{
std::vector &entryArray= _EntryBars[type];
nlassert(entryId abort
if(bde.UIDIn==NULL)
return;
// get the new UID from the SERVER DB
uint newDataSetId= bde.UIDIn->getValue32();
// if present flag is linked, and if 0, then force invalid data set id
if(bde.PresentIn && bde.PresentIn->getValue32()==0)
newDataSetId= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX;
// *** if the data set id does not correspond as the cached one
if(newDataSetId!=bde.DataSetId)
{
// if deleted, delete this entry
if(newDataSetId==CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
delEntry(type, entryId);
// else add
else
addEntry(type, entryId, newDataSetId);
// should be changed
nlassert(bde.DataSetId==newDataSetId);
}
// *** update from DB
if(newDataSetId!=CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
{
updateEntryFromDBNoAddDel(type, bde);
}
}
// ***************************************************************************
void CBarManager::updateTeamMemberFromDB(uint teamMemberId)
{
updateEntryFromDB(TeamMemberType, teamMemberId);
}
// ***************************************************************************
void CBarManager::updateAnimalFromDB(uint paId)
{
updateEntryFromDB(AnimalType, paId);
}
// ***************************************************************************
void CBarManager::updateTargetFromDB()
{
/* Special case for Target. The DataSetId is not replaced with those in DB (setuped with setLocalTarget())
Instead, we ensure that the 2 match, else ignore DB change
*/
CBarDataEntry &bde= _EntryBars[TargetType][0];
// if the UID db was not found, can't do nothing... => abort
if(bde.UIDIn==NULL)
return;
// get the new UID from the SERVER DB
uint serverDataSetId= bde.UIDIn->getValue32();
barInfoLog("BARS: updateTargetFromDB(dsid=%x)", serverDataSetId);
// *** if the server data set id correspond to the local one (and not invalid), update from DB
if(serverDataSetId==bde.DataSetId && serverDataSetId!=CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
{
updateEntryFromDBNoAddDel(TargetType, bde);
}
}
// ***************************************************************************
void CBarManager::setLocalTarget(uint dataSetId)
{
CBarDataEntry &bde= _EntryBars[TargetType][0];
barInfoLog("BARS: setLocalTarget(dsid=%x)", dataSetId);
// *** if the data set id does not correspond as the cached one
if(dataSetId!=bde.DataSetId)
{
// if deleted, delete this entry
if(dataSetId==CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
delEntry(TargetType, 0);
// else add
else
addEntry(TargetType, 0, dataSetId);
// should be changed
nlassert(bde.DataSetId==dataSetId);
}
}
// ***************************************************************************
CBarManager::CBarInfo CBarManager::getBarsByEntityId(CLFECOMMON::TCLEntityId entityId) const
{
const std::vector &entityBars= _EntryBars[EntityType];
nlassert(entityId=_LastUserBarMsgNumber)
diff= (sint32)msgNumber - (sint32)_LastUserBarMsgNumber;
else
diff= (sint32)msgNumber + 256 - (sint32)_LastUserBarMsgNumber;
// if too big difference, suppose a roll (eg last==255, cur=0, real diff==1)
if(diff>127)
diff-=256;
// if diff<0, means that the cur message is actually too old => discard
if(diff<0)
return;
else
{
// bkup last
_LastUserBarMsgNumber= msgNumber;
// user now can only manage 4 scores
nlctassert(SCORES::NUM_SCORES==4);
_UserScores[SCORES::hit_points].Score= hp;
_UserScores[SCORES::sap].Score= sap;
_UserScores[SCORES::stamina].Score= sta;
_UserScores[SCORES::focus].Score= focus;
// update actual database now.
for(uint i=0;isetValue32(max((sint32)0,_UserScores[i].Score));
}
// Update the Player Entry
updateUserBars();
}
}
// ***************************************************************************
void CBarManager::updateUserBars()
{
// for all scores
for(uint i=0;igetValue32();
maxVal= max((sint32)1, maxVal);
}
// setup signed ratio. It is used for the view of Player interface
if(us.DBOutRatio)
{
sint32 v= UserBarMaxRatio*us.Score / maxVal;
clamp(v, (sint32)-UserBarMaxRatio, (sint32)UserBarMaxRatio);
us.DBOutRatio->setValue32(v);
}
// compute signed ratio -127 / 127, for CBarInfo replace
{
sint32 v= 127*us.Score / maxVal;
clamp(v, -127, 127);
_UserBarInfo.Score[i]= (sint8)v;
}
}
// replace the user Entry with the bar info.
// This is used only for the view of bars InScene, and in case the user target himself
uint userDataSetId= _EntryBars[EntityType][0].DataSetId;
if(userDataSetId!=CLFECOMMON::INVALID_CLIENT_DATASET_INDEX)
{
// Do a little cheat: force the update by retrieving the last update time in the BarData
TUIDToDatas::iterator it= _UIDBars.find(userDataSetId);
if(it!=_UIDBars.end())
{
TGameCycle serverTick= 0;
for(uint sc=0;scsecond.ScoreDate[sc] > serverTick)
serverTick= it->second.ScoreDate[sc];
}
// update (user can only manage 4 scores for now)
nlctassert(SCORES::NUM_SCORES==4);
updateBars(userDataSetId, _UserBarInfo, serverTick, HpFlag | SapFlag | StaFlag | FocusFlag);
}
}
}
// ***************************************************************************
sint32 CBarManager::getUserScore(SCORES::TScores score)
{
nlassert((uint) score < SCORES::NUM_SCORES);
nlassert(_UserScores[score].DBOutVal); // initInGame() not called ?
return _UserScores[score].DBOutVal->getValue32();
}
// ***************************************************************************
// ***************************************************************************
// ACTION HANDLERS
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
DECLARE_INTERFACE_CONSTANT(getMaxAnimal, CBarManager::MaxAnimal);
DECLARE_INTERFACE_CONSTANT(getMaxTeamMember, CBarManager::MaxTeamMember);
// ***************************************************************************
class CAHBarManagerOnTarget : public IActionHandler
{
public:
virtual void execute(CCtrlBase * /* pCaller */, const string &/* Params */)
{
CBarManager::getInstance()->updateTargetFromDB();
}
};
REGISTER_ACTION_HANDLER(CAHBarManagerOnTarget, "bar_manager_on_target");
// ***************************************************************************
class CAHBarManagerOnTeam : public IActionHandler
{
public:
virtual void execute(CCtrlBase * /* pCaller */, const string &Params)
{
uint index;
fromString(Params, index);
if(indexupdateTeamMemberFromDB(index);
}
};
REGISTER_ACTION_HANDLER(CAHBarManagerOnTeam, "bar_manager_on_team");
// ***************************************************************************
class CAHBarManagerOnAnimal : public IActionHandler
{
public:
virtual void execute(CCtrlBase * /* pCaller */, const string &Params)
{
uint index;
fromString(Params, index);
if(indexupdateAnimalFromDB(index);
}
};
REGISTER_ACTION_HANDLER(CAHBarManagerOnAnimal, "bar_manager_on_animal");
// ***************************************************************************
class CAHBarManagerOnUserScores : public IActionHandler
{
public:
virtual void execute(CCtrlBase * /* pCaller */, const string &/* Params */)
{
CBarManager::getInstance()->updateUserBars();
}
};
REGISTER_ACTION_HANDLER(CAHBarManagerOnUserScores, "bar_manager_on_user_scores");