// 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");