diff --git a/code/ryzom/client/src/character_cl.cpp b/code/ryzom/client/src/character_cl.cpp index 146d0dc1c..4e7a45885 100644 --- a/code/ryzom/client/src/character_cl.cpp +++ b/code/ryzom/client/src/character_cl.cpp @@ -359,6 +359,13 @@ CCharacterCL::CCharacterCL() _EventFactionId = 0; _PvpMode = PVP_MODE::None; _PvpClan = PVP_CLAN::None; + + for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) + { + _PvpAllies[i] = false; + _PvpEnemies[i] = false; + } + _OutpostId = 0; _OutpostSide = OUTPOSTENUMS::UnknownPVPSide; @@ -1853,12 +1860,16 @@ void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &/* game //----------------------------------------------- void CCharacterCL::updateVisualPropertyPvpClan(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { - _PvpClan = PVP_CLAN::TPVPClan(prop); - if (_PvpClan >= PVP_CLAN::NbClans) + // get fames signs from prop + for (uint8 fameIdx = 0; fameIdx < 7; fameIdx++) { - //nlwarning("updateVisualPropertyPvpClan: received invalid PvP clan: %"NL_I64"u", prop); - _PvpClan = PVP_CLAN::None; + _PvpAllies[fameIdx] = prop & (1 << 2*fameIdx); + _PvpEnemies[fameIdx] = prop & (1 << ((2*fameIdx)+1)); } + + _ClanCivMaxFame = PVP_CLAN::TPVPClan((prop & (0x03 << 2*7)) >> 2*7); + _ClanCultMaxFame = PVP_CLAN::TPVPClan(4 + ((prop & (0x03 << 2*8)) >> 2*8)); + buildInSceneInterface(); } // updateVisualPropertyPvpClan // diff --git a/code/ryzom/client/src/character_cl.h b/code/ryzom/client/src/character_cl.h index 85f408fa7..1a0e864d0 100644 --- a/code/ryzom/client/src/character_cl.h +++ b/code/ryzom/client/src/character_cl.h @@ -372,6 +372,44 @@ public: void setPvpMode(uint8 mode) { _PvpMode=mode; } virtual PVP_CLAN::TPVPClan getPvpClan() const { return _PvpClan; } void setPvpClan(PVP_CLAN::TPVPClan clan) { _PvpClan=clan; } + + virtual PVP_CLAN::TPVPClan getClanCivMaxFame() const { return _ClanCivMaxFame; } + void setClanCivMaxFame(PVP_CLAN::TPVPClan clan) { _ClanCivMaxFame=clan; } + virtual PVP_CLAN::TPVPClan getClanCultMaxFame() const { return _ClanCultMaxFame; } + void setClanCultMaxFame(PVP_CLAN::TPVPClan clan) { _ClanCultMaxFame=clan; } + + virtual bool isPvpAlly(uint8 faction) const { if (faction < PVP_CLAN::NbClans) return _PvpAllies[faction]; else return false; } + virtual bool isPvpEnnemy(uint8 faction) const { if (faction < PVP_CLAN::NbClans) return _PvpEnemies[faction]; else return false; } + + virtual bool isPvpMarauder() const + { + for (uint8 i = 0; i < 4; i++) + if (!_PvpEnemies[i]) + return false; + return true; + } + + virtual bool isPvpTrytonist() const + { + if (_PvpEnemies[4] && _PvpEnemies[6]) + return true; + return false; + } + + virtual bool isPvpPrimas() const + { + if (_PvpAllies[4] && _PvpAllies[6]) + return true; + return false; + } + + virtual bool isPvpRanger() const + { + for (uint8 i = 0; i < 4; i++) + if (!_PvpAllies[i]) + return false; + return true; + } virtual uint16 getOutpostId() const { return _OutpostId; } virtual OUTPOSTENUMS::TPVPSide getOutpostSide() const { return _OutpostSide; } @@ -690,6 +728,10 @@ protected: uint32 _EventFactionId; uint8 _PvpMode; PVP_CLAN::TPVPClan _PvpClan; + PVP_CLAN::TPVPClan _ClanCivMaxFame; + PVP_CLAN::TPVPClan _ClanCultMaxFame; + bool _PvpAllies[PVP_CLAN::NbClans]; + bool _PvpEnemies[PVP_CLAN::NbClans]; uint16 _OutpostId; OUTPOSTENUMS::TPVPSide _OutpostSide; diff --git a/code/ryzom/client/src/client_chat_manager.cpp b/code/ryzom/client/src/client_chat_manager.cpp index aa438a989..97814abe1 100644 --- a/code/ryzom/client/src/client_chat_manager.cpp +++ b/code/ryzom/client/src/client_chat_manager.cpp @@ -40,6 +40,7 @@ #include "game_share/generic_xml_msg_mngr.h" #include "game_share/msg_client_server.h" #include "game_share/chat_group.h" +#include "interface_v3/skill_manager.h" using namespace std; using namespace NLMISC; @@ -948,19 +949,29 @@ void CClientChatManager::buildTellSentence(const ucstring &sender, const ucstrin else { ucstring name = CEntityCL::removeTitleAndShardFromName(sender); + ucstring csr; // special case where there is only a title, very rare case for some NPC if (name.empty()) { // we need the gender to display the correct title CCharacterCL *entity = dynamic_cast(EntitiesMngr.getEntityByName(sender, true, true)); - bool bWoman = entity && entity->getGender() == GSGENDER::female; name = STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(CEntityCL::getTitleFromName(sender), bWoman); + } + else + { + // Does the char have a CSR title? + csr = CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender)) ? ucstring("(CSR) ") : ucstring(""); } - result = name + ucstring(" ") + CI18N::get("tellsYou") + ucstring(": ") + msg; + ucstring cur_time; + if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false)->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + result = cur_time + csr + name + ucstring(" ") + CI18N::get("tellsYou") + ucstring(": ") + msg; } } @@ -992,27 +1003,41 @@ void CClientChatManager::buildChatSentence(TDataSetIndex /* compressedSenderInde // Format the sentence with the provided sender name ucstring senderName = CEntityCL::removeTitleAndShardFromName(sender); + + // Add time if not a &bbl& + ucstring cur_time; + if (cat.toString() != "&bbl&") + { + if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false)->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + } + + ucstring csr; + // Does the char have a CSR title? + csr = CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender)) ? ucstring("(CSR) ") : ucstring(""); + if (UserEntity && senderName == UserEntity->getDisplayName()) { - // the player talks + // The player talks switch(type) { case CChatGroup::shout: - result = cat + CI18N::get("youShout") + ucstring(" : ") + finalMsg; + result = cat + cur_time + csr + CI18N::get("youShout") + ucstring(": ") + finalMsg; break; default: - result = cat + CI18N::get("youSay") + ucstring(" : ") + finalMsg; + result = cat + cur_time + csr + CI18N::get("youSay") + ucstring(": ") + finalMsg; break; } } else { - // special case where there is only a title, very rare case for some NPC + // Special case where there is only a title, very rare case for some NPC if (senderName.empty()) { - // we need the gender to display the correct title CCharacterCL *entity = dynamic_cast(EntitiesMngr.getEntityByName(sender, true, true)); - + // We need the gender to display the correct title bool bWoman = entity && entity->getGender() == GSGENDER::female; senderName = STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(CEntityCL::getTitleFromName(sender), bWoman); @@ -1021,10 +1046,10 @@ void CClientChatManager::buildChatSentence(TDataSetIndex /* compressedSenderInde switch(type) { case CChatGroup::shout: - result = cat + senderName + ucstring(" ") + CI18N::get("heShout") + ucstring(" : ") + finalMsg; + result = cat + cur_time + csr + senderName + ucstring(" ") + CI18N::get("heShout") + ucstring(": ") + finalMsg; break; default: - result = cat + senderName + ucstring(" ") + CI18N::get("heSays") + ucstring(" : ") + finalMsg; + result = cat + cur_time + csr + senderName + ucstring(" ") + CI18N::get("heSays") + ucstring(": ") + finalMsg; break; } } @@ -1157,7 +1182,13 @@ class CHandlerTell : public IActionHandler prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," "); ucstring finalMsg; CChatWindow::encodeColorTag(prop.getRGBA(), finalMsg, false); - finalMsg += CI18N::get("youTell") + ": "; + ucstring cur_time; + if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false)->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + finalMsg += cur_time + csr + CI18N::get("youTell") + ": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); CChatWindow::encodeColorTag(prop.getRGBA(), finalMsg, true); finalMsg += message; @@ -1238,6 +1269,14 @@ void CClientChatManager::updateChatModeAndButton(uint mode) case CChatGroup::universe: pUserBut->setHardText("uiFilterUniverse"); break; case CChatGroup::team: if (teamActive) pUserBut->setHardText("uiFilterTeam"); break; case CChatGroup::guild: if (guildActive) pUserBut->setHardText("uiFilterGuild"); break; + case CChatGroup::dyn_chat: + uint32 index = PeopleInterraction.TheUserChat.Filter.getTargetDynamicChannelDbIndex(); + uint32 textId = pIM->getDbProp("SERVER:DYN_CHAT:CHANNEL"+toString(index)+":NAME")->getValue32(); + ucstring title; + STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title); + pUserBut->setHardText(title.toUtf8()); + break; + // NB: user chat cannot have yubo_chat target } pUserBut->setActive(true); @@ -1276,6 +1315,12 @@ class CHandlerTalk : public IActionHandler text.fromUtf8 (getParam (sParams, "text")); // text = getParam (sParams, "text"); + // Parse any tokens in the text + if ( ! CInterfaceManager::parseTokens(text)) + { + return; + } + // Find the base group if ((modedisplaySystemInfo (ucstring(cmd+" : ")+CI18N::get ("uiCommandNotExists")); + im->displaySystemInfo (ucstring(cmd+": ")+CI18N::get ("uiCommandNotExists")); } } else diff --git a/code/ryzom/client/src/commands.cpp b/code/ryzom/client/src/commands.cpp index a31619936..cb88846e1 100644 --- a/code/ryzom/client/src/commands.cpp +++ b/code/ryzom/client/src/commands.cpp @@ -125,7 +125,7 @@ extern CClientChatManager ChatMngr; extern ULandscape *Landscape; extern UScene *Scene; extern CLog g_log; - +extern CEntityManager EntitiesMngr; /////////////// // Variables // @@ -342,6 +342,317 @@ NLMISC_COMMAND(random, "Roll a dice and say the result around","[] ") return true; } +//----------------------------------------------- +// 'dumpShapePos' : Dump Last Added Shape Pos +//----------------------------------------------- +NLMISC_COMMAND(dumpShapePos, "Dump Last Added Shape Pos.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if (ShapeAddedByCommand.empty()) + { + nlwarning("No shape created yet"); + return false; + } + + CInterfaceManager *IM = CInterfaceManager::getInstance(); + CVector pos = ShapeAddedByCommand.back().getPos(); + IM->displaySystemInfo(ucstring(toString("Shape Pos = %f, %f, %f", pos.x, pos.y, pos.z))); + return true; +} +//----------------------------------------------- +// 'clearShape' : Remove all shapes added with the 'shape' command +//----------------------------------------------- +NLMISC_COMMAND(clearShape, "Remove all shapes added with the 'shape' command.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if (ShapeAddedByCommand.empty()) + { + nlwarning("No shape created yet"); + return false; + } + + if (!Scene) return false; + for(uint k = 0; k < ShapeAddedByCommand.size(); ++k) + { + Scene->deleteInstance(ShapeAddedByCommand[k]); + } + ShapeAddedByCommand.clear(); + return true; +} + +//----------------------------------------------------- +// 'setShapeX' : Set X position for last created shape +//----------------------------------------------------- +NLMISC_COMMAND(setShapeX, "Set X position for last created shape.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if (args.size() != 1) return false; + if (ShapeAddedByCommand.empty()) + { + nlwarning("No shape created yet"); + return false; + } + float coord; + bool valid_coord; + if (args[0][0] == '+') + valid_coord = fromString(args[0].substr(1), coord); + else + valid_coord = fromString(args[0], coord); + + if (!valid_coord) + { + nlwarning("Can't get position"); + return false; + } + CVector pos = ShapeAddedByCommand.back().getPos(); + if (args[0][0] == '+') + pos.x += coord; + else + pos.x = coord; + ShapeAddedByCommand.back().setPos(pos); + return true; +} + +//----------------------------------------------------- +// 'setShapeY' : Set Y position for last created shape +//----------------------------------------------------- +NLMISC_COMMAND(setShapeY, "Set Y position for last created shape.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if (args.size() != 1) return false; + if (ShapeAddedByCommand.empty()) + { + nlwarning("No shape created yet"); + return false; + } + float coord; + bool valid_coord; + if (args[0][0] == '+') + valid_coord = fromString(args[0].substr(1), coord); + else + valid_coord = fromString(args[0], coord); + + if (!valid_coord) + { + nlwarning("Can't get position"); + return false; + } + CVector pos = ShapeAddedByCommand.back().getPos(); + if (args[0][0] == '+') + pos.y += coord; + else + pos.y = coord; + ShapeAddedByCommand.back().setPos(pos); + return true; +} + +//----------------------------------------------------- +// 'setShapeZ' : Set Z position for last created shape +//----------------------------------------------------- +NLMISC_COMMAND(setShapeZ, "Set Z position for last created shape.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if (args.size() != 1) return false; + if (ShapeAddedByCommand.empty()) + { + nlwarning("No shape created yet"); + return false; + } + float coord; + bool valid_coord; + if (args[0][0] == '+') + valid_coord = fromString(args[0].substr(1), coord); + else + valid_coord = fromString(args[0], coord); + + if (!valid_coord) + { + nlwarning("Can't get position"); + return false; + } + CVector pos = ShapeAddedByCommand.back().getPos(); + if (args[0][0] == '+') + pos.z += coord; + else + pos.z = coord; + ShapeAddedByCommand.back().setPos(pos); + return true; +} + + +//----------------------------------------------------- +// 'setShapeDir' : Set direction angle for last created shape +//----------------------------------------------------- +NLMISC_COMMAND(setShapeDir, "Set direction angle for last created shape.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if (args.size() != 1) return false; + if (ShapeAddedByCommand.empty()) + { + nlwarning("No shape created yet"); + return false; + } + float angle; + if (!fromString(args[0], angle)) + { + nlwarning("Can't get angle"); + return false; + } + + CMatrix dir; + dir.identity(); + CVector vangle = CVector(sin(angle), cos(angle), 0.f); + CVector vi = vangle^CVector(0.f, 0.f, 1.f); + CVector vk = vi^vangle; + dir.setRot(vi, vangle, vk, true); + // Set Orientation : User Direction should be normalized. + ShapeAddedByCommand.back().setRotQuat(dir.getRot()); + + return true; +} + + +//----------------------------------------------- +// 'shape' : Add a shape in the scene. +//----------------------------------------------- +NLMISC_COMMAND(shape, "Add a shape in the scene.", "") +{ + #if FINAL_VERSION + if (!hasPrivilegeDEV() && + !hasPrivilegeSGM() && + !hasPrivilegeGM() && + !hasPrivilegeVG() && + !hasPrivilegeSG() && + !hasPrivilegeG() && + !hasPrivilegeEM() && + !hasPrivilegeEG()) + return true; + #endif // FINAL_VERSION + + if(args.size() < 1) + { + nlwarning("Command 'shape': need at least 1 parameter, try '/help shape' for more details."); + return false; + } + if (!Scene) + { + nlwarning("No scene available"); + return false; + } + UInstance instance = Scene->createInstance(args[0]); + if(!instance.empty()) + { + ShapeAddedByCommand.push_back(instance); + // Set the position + instance.setPos(UserEntity->pos()); + instance.setClusterSystem(UserEntity->getClusterSystem()); // for simplicity, assume it is in the same + // cluster system than the user + // Compute the direction Matrix + CMatrix dir; + dir.identity(); + CVector vi = UserEntity->dir()^CVector(0.f, 0.f, 1.f); + CVector vk = vi^UserEntity->dir(); + dir.setRot(vi, UserEntity->dir(), vk, true); + // Set Orientation : User Direction should be normalized. + instance.setRotQuat(dir.getRot()); + // if the shape is a particle system, additionnal parameters are user params + UParticleSystemInstance psi; + psi.cast (instance); + if (!psi.empty()) + { + // set each user param that is present + for(uint k = 0; k < 4; ++k) + { + if (args.size() >= (k + 2)) + { + float uparam; + if (fromString(args[k + 1], uparam)) + { + psi.setUserParam(k, uparam); + } + else + { + nlwarning("Cant read param %d", k); + } + } + } + } + } + else + { + nlwarning("Command 'shape': cannot find the shape %s.", args[0].c_str()); + } + + // Command Well Done + return true; +} + NLMISC_COMMAND(bugReport, "Call the bug report tool with dump", "") { const char *brname[] = { "bug_report.exe", "bug_report_r.exe", "bug_report_rd.exe", "bug_report_df.exe", "bug_report_d.exe" }; @@ -908,6 +1219,10 @@ static bool talkInChan(uint32 nb,std::vectorargs) PeopleInterraction.talkInDynamicChannel(nb,ucstring(tmp)); return true; } + else + { + ChatMngr.updateChatModeAndButton(CChatGroup::dyn_chat); + } return false; } @@ -3467,156 +3782,6 @@ NLMISC_COMMAND(dist2side, "Change the distance to the side for a given sheet.", - - - -//----------------------------------------------- -// 'clearShape' : Remove all shapes added with the 'shape' command -//----------------------------------------------- -NLMISC_COMMAND(clearShape, "Remove all shapes added with the 'shape' command.", "") -{ - if (!Scene) return false; - for(uint k = 0; k < ShapeAddedByCommand.size(); ++k) - { - Scene->deleteInstance(ShapeAddedByCommand[k]); - } - ShapeAddedByCommand.clear(); - return true; -} - - -//----------------------------------------------------- -// 'setShapeX' : Set X position for last created shape -//----------------------------------------------------- -NLMISC_COMMAND(setShapeX, "Set X position for last created shape.", "") -{ - if (args.size() != 1) return false; - if (ShapeAddedByCommand.empty()) - { - nlwarning("No shape created yet"); - return false; - } - float coord; - if (!fromString(args[0], coord)) - { - nlwarning("Can't get position"); - return false; - } - CVector pos = ShapeAddedByCommand.back().getPos(); - pos.x = coord; - ShapeAddedByCommand.back().setPos(pos); - return true; -} - -//----------------------------------------------------- -// 'setShapeY' : Set Y position for last created shape -//----------------------------------------------------- -NLMISC_COMMAND(setShapeY, "Set Y position for last created shape.", "") -{ - if (args.size() != 1) return false; - if (ShapeAddedByCommand.empty()) - { - nlwarning("No shape created yet"); - return false; - } - float coord; - if (!fromString(args[0], coord)) - { - nlwarning("Can't get position"); - return false; - } - CVector pos = ShapeAddedByCommand.back().getPos(); - pos.y = coord; - ShapeAddedByCommand.back().setPos(pos); - return true; -} - -//----------------------------------------------------- -// 'setShapeZ' : Set Z position for last created shape -//----------------------------------------------------- -NLMISC_COMMAND(setShapeZ, "Set Z position for last created shape.", "") -{ - if (args.size() != 1) return false; - if (ShapeAddedByCommand.empty()) - { - nlwarning("No shape created yet"); - return false; - } - float coord; - if (!fromString(args[0], coord)) - { - nlwarning("Can't get position"); - return false; - } - CVector pos = ShapeAddedByCommand.back().getPos(); - pos.z = coord; - ShapeAddedByCommand.back().setPos(pos); - return true; -} - - -//----------------------------------------------- -// 'shape' : Add a shape in the scene. -//----------------------------------------------- -NLMISC_COMMAND(shape, "Add a shape in the scene.", "") -{ - if(args.size() < 1) - { - nlwarning("Command 'shape': need at least 1 parameter, try '/help shape' for more details."); - return false; - } - if (!Scene) - { - nlwarning("No scene available"); - return false; - } - UInstance instance = Scene->createInstance(args[0]); - if(!instance.empty()) - { - ShapeAddedByCommand.push_back(instance); - // Set the position - instance.setPos(UserEntity->pos()); - instance.setClusterSystem(UserEntity->getClusterSystem()); // for simplicity, assume it is in the same - // cluster system than the user - // Compute the direction Matrix - CMatrix dir; - dir.identity(); - CVector vi = UserEntity->dir()^CVector(0.f, 0.f, 1.f); - CVector vk = vi^UserEntity->dir(); - dir.setRot(vi, UserEntity->dir(), vk, true); - // Set Orientation : User Direction should be normalized. - instance.setRotQuat(dir.getRot()); - // if the shape is a particle system, additionnal parameters are user params - UParticleSystemInstance psi; - psi.cast (instance); - if (!psi.empty()) - { - // set each user param that is present - for(uint k = 0; k < 4; ++k) - { - if (args.size() >= (k + 2)) - { - float uparam; - if (fromString(args[k + 1], uparam)) - { - psi.setUserParam(k, uparam); - } - else - { - nlwarning("Cant read param %d", k); - } - } - } - } - } - else - nlwarning("Command 'shape': cannot find the shape %s.", args[0].c_str()); - - // Command Well Done - return true; -} - - // Change the parent of an entity. 'parent slot' not defined remove the current parent. NLMISC_COMMAND(parent, "Change the parent of an entity.", " []") { diff --git a/code/ryzom/client/src/cursor_functions.cpp b/code/ryzom/client/src/cursor_functions.cpp index 79998b7c7..f4190f69a 100644 --- a/code/ryzom/client/src/cursor_functions.cpp +++ b/code/ryzom/client/src/cursor_functions.cpp @@ -777,7 +777,7 @@ void contextExtractRM(bool rightClick, bool dblClick) return; if( ClientCfg.DblClickMode && !dblClick) return; - UserEntity->moveToExtractionPhrase(SlotUnderCursor, MaxExtractionDistance, ~0, ~0, true ); + UserEntity->moveToExtractionPhrase(SlotUnderCursor, MaxExtractionDistance, std::numeric_limits::max(), std::numeric_limits::max(), true ); } //----------------------------------------------- diff --git a/code/ryzom/client/src/entities.cpp b/code/ryzom/client/src/entities.cpp index fc2aa43de..94dd6855b 100644 --- a/code/ryzom/client/src/entities.cpp +++ b/code/ryzom/client/src/entities.cpp @@ -494,6 +494,90 @@ void CEntityManager::reinit() initialize(_NbMaxEntity); } +CShapeInstanceReference CEntityManager::createInstance(const string& shape, const CVector &pos, const string& text, const string& url, bool bbox_active) +{ + CShapeInstanceReference nullinstref(UInstance(), string(""), string("")); + if (!Scene) return nullinstref; + + UInstance instance = Scene->createInstance(shape); + if (text.empty()) + bbox_active = false; + CShapeInstanceReference instref = CShapeInstanceReference(instance, text, url, bbox_active); + if(!instance.empty()) + { + _ShapeInstances.push_back(instref); + } + return instref; +} + +bool CEntityManager::removeInstances() +{ + if (!Scene) return false; + // Remove all instances. + for(uint i=0; i<_ShapeInstances.size(); ++i) + { + if (!_ShapeInstances[i].Instance.empty()) + Scene->deleteInstance(_ShapeInstances[i].Instance); + } + _ShapeInstances.clear(); + return true; +} + +CShapeInstanceReference CEntityManager::getShapeInstanceUnderPos(float x, float y) +{ + CShapeInstanceReference selectedInstance(UInstance(), string(""), string("")); + _LastInstanceUnderPos= NULL; + + // If not initialised, return + if (_ShapeInstances.empty()) + return selectedInstance; + + // build the ray + CMatrix camMatrix = MainCam.getMatrix(); + CFrustum camFrust = MainCam.getFrustum(); + CViewport viewport = Driver->getViewport(); + + // Get the Ray made by the mouse. + CVector pos, dir; + viewport.getRayWithPoint(x, y, pos, dir, camMatrix, camFrust); + // Normalize the direction. + dir.normalize(); + + // **** Get instances with box intersecting the ray. + float bestDist = 255; + for(uint i=0; i<_ShapeInstances.size(); i++) + { + if (_ShapeInstances[i].BboxActive) + { + H_AUTO(RZ_Client_GEUP_box_intersect) + + // if intersect the bbox + NLMISC::CAABBox bbox; + //= _ShapeInstances[i].SelectionBox; + _ShapeInstances[i].Instance.getShapeAABBox(bbox); + if (bbox.getCenter() == CVector::Null) + { + bbox.setMinMax(CVector(-0.3f, -0.3f, -0.3f)+_ShapeInstances[i].Instance.getPos(), CVector(0.3f, 0.3f, 0.3f)+_ShapeInstances[i].Instance.getPos()); + } + else + { + bbox.setMinMax((bbox.getMin()*_ShapeInstances[i].Instance.getScale().x)+_ShapeInstances[i].Instance.getPos(), (bbox.getMax()*_ShapeInstances[i].Instance.getScale().x)+_ShapeInstances[i].Instance.getPos()); + } + if(bbox.intersect(pos, pos+dir*15.0f)) + { + float dist = (bbox.getCenter()-pos).norm(); + if (dist < bestDist) + { + selectedInstance = _ShapeInstances[i]; + bestDist = dist; + } + } + } + } + return selectedInstance; +} + + //----------------------------------------------- // Create an entity according to the slot and the form. // \param uint slot : slot for the entity. diff --git a/code/ryzom/client/src/entities.h b/code/ryzom/client/src/entities.h index 502065926..8d0d780c5 100644 --- a/code/ryzom/client/src/entities.h +++ b/code/ryzom/client/src/entities.h @@ -85,8 +85,28 @@ public: uint Slot; }; +/* + * Class to make cache shape instances + */ +class CShapeInstanceReference +{ +public: + CShapeInstanceReference (NL3D::UInstance instance, const string &text, const string &url, bool bbox_active=true) + { + Instance = instance; + ContextText = text; + ContextURL = url; + BboxActive = bbox_active; + } + + NL3D::UInstance Instance; + string ContextText; + string ContextURL; + bool BboxActive; +}; + /** - * Class to manage entities. + * Class to manage entities and shapes instances. * \author Guillaume PUZIN * \author Nevrax France * \date 2001 @@ -106,6 +126,9 @@ private: std::vector _ActiveEntities; std::vector _VisibleEntities; + /// Shapes Instances caches + std::vector _ShapeInstances; + typedef struct { NLMISC::TGameCycle GC; @@ -123,6 +146,8 @@ private: // For selection. NB: the pointer is just a cache. Must not be accessed CEntityCL *_LastEntityUnderPos; + NL3D::UInstance _LastInstanceUnderPos; + ////////////// //// DEBUG /// uint _NbUser; @@ -174,6 +199,11 @@ public: /// Release + initialize void reinit(); + + CShapeInstanceReference createInstance(const string& shape, const CVector &pos, const string &text, const string &url, bool active=true); + bool removeInstances(); + CShapeInstanceReference getShapeInstanceUnderPos(float x, float y); + /** * Create an entity according to the slot and the form. * \param uint slot : slot for the entity. diff --git a/code/ryzom/client/src/entity_cl.cpp b/code/ryzom/client/src/entity_cl.cpp index e046464b3..37324e1ff 100644 --- a/code/ryzom/client/src/entity_cl.cpp +++ b/code/ryzom/client/src/entity_cl.cpp @@ -2511,12 +2511,20 @@ NLMISC::CRGBA CEntityCL::getColor () const { if (isEnemy()) { - return _PvpEnemyColor; + if (getPvpMode()&PVP_MODE::PvpFactionFlagged || getPvpMode()&PVP_MODE::PvpChallenge) + return _PvpEnemyColor; + else + return CRGBA::CRGBA(min(255, _PvpEnemyColor.R+150), min(255, _PvpEnemyColor.G+150), min(255, _PvpEnemyColor.B+150),_PvpEnemyColor.A); } } // neutral pvp if (isNeutralPVP()) { + if (isInTeam()) + return _PvpAllyInTeamColor; + if (isInGuild()) + return _PvpAllyInGuildColor; + return _PvpNeutralColor; } // ally @@ -2526,7 +2534,11 @@ NLMISC::CRGBA CEntityCL::getColor () const return _PvpAllyInTeamColor; if(isInGuild()) return _PvpAllyInGuildColor; - return _PvpAllyColor; + + if (getPvpMode()&PVP_MODE::PvpFactionFlagged) + return _PvpAllyColor; + else + return CRGBA::CRGBA(min(255, _PvpAllyColor.R+150), min(255, _PvpAllyColor.G+150), min(255, _PvpAllyColor.B+150),_PvpAllyColor.A); } // neutral if (isInTeam()) @@ -2795,9 +2807,11 @@ void CEntityCL::updateIsInTeam () presentProp && presentProp->getValueBool() ) { _IsInTeam= true; + buildInSceneInterface(); return; } } + buildInSceneInterface(); } //----------------------------------------------- @@ -3051,13 +3065,15 @@ void CEntityCL::updateVisiblePostPos(const NLMISC::TTime &/* currentTimeInMs */, position = pos().asVector(); position.z= _SelectBox.getMin().z; mat.setPos(position); + mat.setRot(dirMatrix()); _StateFX.setTransformMode(NL3D::UTransformable::DirectMatrix); _StateFX.setMatrix(mat); if (skeleton()) _StateFX.setClusterSystem(skeleton()->getClusterSystem()); } - else if (!_SelectionFX.empty() || !_MouseOverFX.empty()) + + if (!_SelectionFX.empty() || !_MouseOverFX.empty()) { // Build a matrix for the fx NLMISC::CMatrix mat; diff --git a/code/ryzom/client/src/entity_cl.h b/code/ryzom/client/src/entity_cl.h index 725f6b883..1c79161ea 100644 --- a/code/ryzom/client/src/entity_cl.h +++ b/code/ryzom/client/src/entity_cl.h @@ -686,7 +686,7 @@ public: virtual bool isNeutralPVP() const { return false; } /// Return true if this player has the viewing properties of a friend (inscene bars...) - virtual bool isViewedAsFriend() const { return isNeutral() || isFriend(); } + virtual bool isViewedAsFriend() const { return isNeutral() || isFriend() || isInTeam() || isInGuild(); } /// Return the People for the entity (unknown by default) virtual EGSPD::CPeople::TPeople people() const; @@ -760,6 +760,12 @@ public: return _Title; } + /// Return the raw unparsed entity title + const ucstring getTitleRaw() const + { + return ucstring(_TitleRaw); + } + /// Return true if this entity has a reserved title bool hasReservedTitle() const { return _HasReservedTitle; } diff --git a/code/ryzom/client/src/init_main_loop.cpp b/code/ryzom/client/src/init_main_loop.cpp index 88bf1ac5a..9b37a1a53 100644 --- a/code/ryzom/client/src/init_main_loop.cpp +++ b/code/ryzom/client/src/init_main_loop.cpp @@ -1364,6 +1364,10 @@ void loadBackgroundBitmap (TBackground background) string ext = CFile::getExtension (ClientCfg.Launch_BG); string filename; + if (frand(2.0) < 1) + filename = name+"_0."+ext; + else + filename = name+"_1."+ext; switch (background) { case ElevatorBackground: @@ -1397,13 +1401,6 @@ void loadBackgroundBitmap (TBackground background) filename = ClientCfg.Loading_BG; break; default: - /* - if (frand(2.0) < 1) - filename = name+"_0."+ext; - else - filename = name+"_1."+ext; - */ - filename = name+"."+ext; break; } diff --git a/code/ryzom/client/src/interface_v3/action_handler_game.cpp b/code/ryzom/client/src/interface_v3/action_handler_game.cpp index 11703d5fa..be0e50b5e 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_game.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_game.cpp @@ -2440,6 +2440,174 @@ class CAHTarget : public IActionHandler }; REGISTER_ACTION_HANDLER (CAHTarget, "target"); + + +class CAHAddShape : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const string &Params) + { + string sShape = getParam(Params, "shape"); + if(sShape.empty()) + { + nlwarning("Command 'add_shape': need at least the parameter shape."); + return; + } + if (!Scene) + { + nlwarning("No scene available"); + return; + } + + double x = UserEntity->pos().x; + double y = UserEntity->pos().y; + double z = UserEntity->pos().z; + CVector userDir = UserEntity->dir(); + float s = 1.0f; + string skeleton = getParam(Params, "skeleton"); + string c = getParam(Params, "text"); + string u = getParam(Params, "url"); + string texture_name = getParam(Params, "texture"); + string highlight = getParam(Params, "texture"); + string transparency = getParam(Params, "transparency"); + + if (!getParam(Params, "x").empty()) + fromString(getParam(Params, "x"), x); + if (!getParam(Params, "y").empty()) + fromString(getParam(Params, "y"), y); + if (!getParam(Params, "z").empty()) + fromString(getParam(Params, "z"), z); + if (!getParam(Params, "scale").empty()) + fromString(getParam(Params, "scale"), s); + if (!getParam(Params, "angle").empty()) + { + float a; + fromString(getParam(Params, "angle"), a); + userDir = CVector(sin(a), cos(a), 0.f); + } + + bool have_shapes = true; + bool first_shape = true; + while(have_shapes) + { + string shape; + string::size_type index = sShape.find(string(" ")); + // multiple shapes/fx + if (index != string::npos) + { + shape = sShape.substr(0, index); + sShape = sShape.substr(index+1); + } else { + shape = sShape; + have_shapes = false; + } + + + CShapeInstanceReference instref = EntitiesMngr.createInstance(shape, CVector((float)x, (float)y, (float)z), c, u, first_shape); + UInstance instance = instref.Instance; + + if(!instance.empty()) + { + for(uint j=0;jgetClusterSystem()); // for simplicity, assume it is in the same + // cluster system than the user + // Compute the direction Matrix + CMatrix dir; + dir.identity(); + CVector vi = userDir^CVector(0.f, 0.f, 1.f); + CVector vk = vi^userDir; + dir.setRot(vi, userDir, vk, true); + // Set Orientation : User Direction should be normalized. + if (!skeleton.empty()) + { + USkeleton skel = Scene->createSkeleton(skeleton); + if (!skel.empty()) + { + skel.bindSkin(instance); + skel.setClusterSystem(UserEntity->getClusterSystem()); + skel.setScale(skel.getScale()*s); + skel.setPos(CVector((float)x, (float)y, (float)z)); + skel.setRotQuat(dir.getRot()); + } + } else { + instance.setScale(instance.getScale()*s); + instance.setPos(CVector((float)x, (float)y, (float)z)); + instance.setRotQuat(dir.getRot()); + } + // if the shape is a particle system, additionnal parameters are user params + UParticleSystemInstance psi; + psi.cast (instance); + /*if (!psi.empty()) + { + // set each user param that is present + for(uint k = 0; k < 4; ++k) + { + if (args.size() >= (k + 2)) + { + float uparam; + if (fromString(args[k + 1], uparam)) + { + psi.setUserParam(k, uparam); + } + else + { + nlwarning("Cant read param %d", k); + } + } + } + }*/ + } + else + nlwarning("Command 'add_shape': cannot find the shape %s.", sShape.c_str()); + } + + return; + } +}; +REGISTER_ACTION_HANDLER (CAHAddShape, "add_shape"); + +class CAHRemoveShapes : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const string &Params) + { + EntitiesMngr.removeInstances(); + } +}; +REGISTER_ACTION_HANDLER (CAHRemoveShapes, "remove_shapes"); + // *************************************************************************** // See also CHandlerTeamTarget class CAHTargetTeammateShortcut : public IActionHandler diff --git a/code/ryzom/client/src/interface_v3/action_handler_help.cpp b/code/ryzom/client/src/interface_v3/action_handler_help.cpp index 4a386fd5e..f9277aa9a 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_help.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_help.cpp @@ -104,7 +104,9 @@ static void setupMissionHelp(CSheetHelpSetup &setup); #define INFO_LIST_BRICK_REQUIREMENT "list_brick_requirement" #define INFO_GROUP_MP_STAT "mp_stats" #define INFO_GROUP_CHAR_3D "char3d" - +#define INFO_ITEM_PREVIEW "item_preview" +#define INFO_ITEM_PREVIEW_SCENE_3D "scene_item_preview" +#define ITEM_PREVIEW_WIDTH 200 // *************************************************************************** std::deque CInterfaceHelp::_ActiveWindows; @@ -2178,6 +2180,14 @@ void resetSheetHelp(CSheetHelpSetup &setup) { listItem->setActive(false); } + + // Hide the item preview by default + CInterfaceElement *elt= group->getElement(group->getId()+setup.PrefixForExtra+INFO_ITEM_PREVIEW); + if(elt) + { + elt->setActive(false); + } + // Hide the list of brick by default IListSheetBase *listBrick= dynamic_cast(group->getElement(group->getId()+setup.PrefixForExtra+INFO_LIST_BRICK)); if(listBrick) @@ -2268,6 +2278,151 @@ void setupCosmetic(CSheetHelpSetup &setup, CItemSheet *pIS) } } +// *************************************************************************** +void setupItemPreview(CSheetHelpSetup &setup, CItemSheet *pIS) +{ + nlassert(pIS); + + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + CCDBNodeBranch *dbBranch = pIM->getDbBranch( setup.SrcSheet->getSheet() ); + + + CInterfaceElement *elt = setup.HelpWindow->getElement(setup.HelpWindow->getId()+setup.PrefixForExtra+INFO_ITEM_PREVIEW); + if (elt == NULL) + return; + + CInterfaceGroup *ig = dynamic_cast(elt); + + if ( ! ig) + { + return; + } + + static sint32 helpWidth = setup.HelpWindow->getW(); + bool scene_inactive = ! pIM->getDbProp("UI:SAVE:SHOW_3D_ITEM_PREVIEW")->getValueBool(); + if (scene_inactive || + (pIS->Family != ITEMFAMILY::ARMOR && + pIS->Family != ITEMFAMILY::MELEE_WEAPON && + pIS->Family != ITEMFAMILY::RANGE_WEAPON && + pIS->Family != ITEMFAMILY::SHIELD)) + { + setup.HelpWindow->setW(helpWidth); + ig->setActive(false); + return; + } + else + { + setup.HelpWindow->setW(helpWidth + ITEM_PREVIEW_WIDTH); + ig->setActive(true); + } + + CInterface3DScene *sceneI = dynamic_cast(ig->getGroup("scene_item_preview")); + if (!sceneI) + { + nlwarning("Can't retrieve character 3d view, or bad type"); + ig->setActive(false); + return; + } + + CInterface3DCharacter *char3DI = NULL; + if (sceneI->getCharacter3DCount() != 0) + char3DI = sceneI->getCharacter3D(0); + + if (char3DI == NULL) + { + nlwarning("Can't retrieve char 3D Interface"); + ig->setActive(false); + return; + } + + CInterface3DCamera *camera = sceneI->getCamera(0); + if (camera == NULL) + { + nlwarning("Can't retrieve camera"); + ig->setActive(false); + return; + } + + + SCharacter3DSetup c3Ds; + + CCharacterSummary cs; + SCharacter3DSetup::setupCharacterSummaryFromSERVERDB( cs ); + + float camHeight = -0.85f; + + if (pIS->Family == ITEMFAMILY::ARMOR) + { + if (pIS->ItemType == ITEM_TYPE::LIGHT_BOOTS || pIS->ItemType == ITEM_TYPE::MEDIUM_BOOTS || pIS->ItemType == ITEM_TYPE::HEAVY_BOOTS) + { + CCDBNodeLeaf *color = dbBranch->getLeaf( setup.SrcSheet->getSheet()+":USER_COLOR", false ); + cs.VisualPropB.PropertySubData.FeetModel = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::FEET_SLOT ); + cs.VisualPropB.PropertySubData.FeetColor = color->getValue32(); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + camHeight = -1.15f; + } + else if (pIS->ItemType == ITEM_TYPE::LIGHT_GLOVES || pIS->ItemType == ITEM_TYPE::MEDIUM_GLOVES || pIS->ItemType == ITEM_TYPE::HEAVY_GLOVES) + { + CCDBNodeLeaf *color = dbBranch->getLeaf( setup.SrcSheet->getSheet()+":USER_COLOR", false ); + cs.VisualPropB.PropertySubData.HandsModel = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::HANDS_SLOT ); + cs.VisualPropB.PropertySubData.HandsColor = color->getValue32(); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + //cs.VisualPropB.PropertySubData.HandsColor = pIS->Color; + } + else if (pIS->ItemType == ITEM_TYPE::LIGHT_SLEEVES || pIS->ItemType == ITEM_TYPE::MEDIUM_SLEEVES || pIS->ItemType == ITEM_TYPE::HEAVY_SLEEVES) + { + CCDBNodeLeaf *color = dbBranch->getLeaf( setup.SrcSheet->getSheet()+":USER_COLOR", false ); + cs.VisualPropA.PropertySubData.ArmModel = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::ARMS_SLOT ); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + SCharacter3DSetup::setDB("UI:TEMP:CHAR3D:VPA:ARMCOLOR", pIS->Color); + //cs.VisualPropA.PropertySubData.ArmColor = pIS->Color; + camHeight = -0.55f; + } + else if (pIS->ItemType == ITEM_TYPE::LIGHT_PANTS || pIS->ItemType == ITEM_TYPE::MEDIUM_PANTS || pIS->ItemType == ITEM_TYPE::HEAVY_PANTS) + { + CCDBNodeLeaf *color = dbBranch->getLeaf( setup.SrcSheet->getSheet()+":USER_COLOR", false ); + cs.VisualPropA.PropertySubData.TrouserModel = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::LEGS_SLOT ); + cs.VisualPropA.PropertySubData.TrouserColor = color->getValue32(); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + camHeight = -1.00f; + } + else if (pIS->ItemType == ITEM_TYPE::LIGHT_VEST || pIS->ItemType == ITEM_TYPE::MEDIUM_VEST || pIS->ItemType == ITEM_TYPE::HEAVY_VEST) + { + CCDBNodeLeaf *color = dbBranch->getLeaf( setup.SrcSheet->getSheet()+":USER_COLOR", false ); + cs.VisualPropA.PropertySubData.JacketModel = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::CHEST_SLOT ); + cs.VisualPropA.PropertySubData.JacketColor = color->getValue32(); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + camHeight = -0.55f; + } + else if (pIS->ItemType == ITEM_TYPE::HEAVY_HELMET) + { + CCDBNodeLeaf *color = dbBranch->getLeaf( setup.SrcSheet->getSheet()+":USER_COLOR", false ); + cs.VisualPropA.PropertySubData.HatModel = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::HEAD_SLOT ); + cs.VisualPropA.PropertySubData.HatColor = color->getValue32(); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + camHeight = -0.35f; + } + } + else if (pIS->Family == ITEMFAMILY::SHIELD) + { + cs.VisualPropA.PropertySubData.WeaponLeftHand = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::LEFT_HAND_SLOT ); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + } + else if (pIS->Family == ITEMFAMILY::MELEE_WEAPON || pIS->Family == ITEMFAMILY::RANGE_WEAPON) + { + cs.VisualPropA.PropertySubData.WeaponRightHand = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::RIGHT_HAND_SLOT ); + SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs); + } + else + nlwarning(" Invalid armour or weapon item type '%s'", ITEM_TYPE::toString( pIS->ItemType ).c_str() ); + + if (camera == NULL) + return; + + camera->setTgtZ(camHeight); + char3DI->setAnim(CAnimationStateSheet::Idle); +} + // *************************************************************************** void refreshItemHelp(CSheetHelpSetup &setup) { @@ -2291,6 +2446,9 @@ void refreshItemHelp(CSheetHelpSetup &setup) // ---- Cosmetic only setupCosmetic (setup, pIS); + + // ---- item preview + setupItemPreview(setup, pIS); } // if this is a R2 plot item, Add comment and description diff --git a/code/ryzom/client/src/interface_v3/chat_filter.cpp b/code/ryzom/client/src/interface_v3/chat_filter.cpp index 7d8d8841e..da0645ed6 100644 --- a/code/ryzom/client/src/interface_v3/chat_filter.cpp +++ b/code/ryzom/client/src/interface_v3/chat_filter.cpp @@ -312,8 +312,7 @@ void CChatTargetFilter::msgEntered(const ucstring &msg, CChatWindow *chatWindow) else if (!_TargetPlayer.empty()) { // the target must be a player, make a tell on him - // TODO: adapt this to unicode when this is OK on server side - ChatMngr.tell(_TargetPlayer.toString(), msg.toString()); + ChatMngr.tell(_TargetPlayer.toString(), msg); // direct output in the chat chatWindow->displayLocalPlayerTell(msg); } diff --git a/code/ryzom/client/src/interface_v3/chat_window.cpp b/code/ryzom/client/src/interface_v3/chat_window.cpp index 9033c7c77..199195ea0 100644 --- a/code/ryzom/client/src/interface_v3/chat_window.cpp +++ b/code/ryzom/client/src/interface_v3/chat_window.cpp @@ -472,7 +472,13 @@ void CChatWindow::displayLocalPlayerTell(const ucstring &msg, uint numBlinks /*= CInterfaceProperty prop; prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," "); encodeColorTag(prop.getRGBA(), finalMsg, false); - finalMsg += CI18N::get("youTell") + ": "; + ucstring cur_time; + if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false)->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + finalMsg += cur_time + csr + CI18N::get("youTell") + ": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); encodeColorTag(prop.getRGBA(), finalMsg, true); finalMsg += msg; @@ -1241,6 +1247,13 @@ public: return; } + // Parse any tokens in the text + if ( ! CInterfaceManager::parseTokens(text)) + { + pEB->setInputString (string("")); + return; + } + // if, it s a command, execute it and don't send the command to the server if(text[0] == '/') { @@ -1256,7 +1269,7 @@ public: else { CInterfaceManager *im = CInterfaceManager::getInstance(); - im->displaySystemInfo (ucstring(cmd+" : ")+CI18N::get ("uiCommandNotExists")); + im->displaySystemInfo (ucstring(cmd+": ")+CI18N::get ("uiCommandNotExists")); } } else diff --git a/code/ryzom/client/src/interface_v3/group_html.cpp b/code/ryzom/client/src/interface_v3/group_html.cpp index baad72f30..f0f9e2308 100644 --- a/code/ryzom/client/src/interface_v3/group_html.cpp +++ b/code/ryzom/client/src/interface_v3/group_html.cpp @@ -45,6 +45,10 @@ extern "C" #include "lua_ihm.h" #include "../time_client.h" +#include "nel/misc/i18n.h" +#include "nel/misc/md5.h" +#include "nel/3d/texture_file.h" +#include "nel/misc/big_file.h" using namespace std; using namespace NLMISC; @@ -107,15 +111,106 @@ void CGroupHTML::addImageDownload(const string &url, CViewBase *img) curl_easy_setopt(curl, CURLOPT_FILE, fp); curl_multi_add_handle(MultiCurl, curl); - Curls.push_back(CImageDownload(curl, url, fp, img)); + Curls.push_back(CDataDownload(curl, url, fp, ImgType, img)); #ifdef LOG_DL nlwarning("adding handle %x, %d curls", curl, Curls.size()); #endif RunningCurls++; } -// Call this evenly to check if an image in downloaded and then display it -void CGroupHTML::checkImageDownload() +void CGroupHTML::initImageDownload() +{ +#ifdef LOG_DL + nlwarning("Init Image Download"); +#endif +/* +// Get current flag +int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); +// Turn on leak-checking bit +tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF; +// Set flag to the new value +_CrtSetDbgFlag( tmpFlag ); +*/ + string pathName = "cache"; + if ( ! CFile::isExists( pathName ) ) + CFile::createDirectory( pathName ); +} + + +// Get an url and return the local filename with the path where the bnp should be +string CGroupHTML::localBnpName(const string &url) +{ + size_t lastIndex = url.find_last_of("/"); + string dest = "user/"+url.substr(lastIndex+1); + return dest; +} + +// Add a bnp download request in the multi_curl +void CGroupHTML::addBnpDownload(const string &url, const string &action) +{ + // Search if we are not already downloading this url. + for(uint i = 0; i < Curls.size(); i++) + { + if(Curls[i].url == url) + { +#ifdef LOG_DL + nlwarning("already downloading '%s'", url.c_str()); +#endif + return; + } + } + + CURL *curl = curl_easy_init(); + if (!MultiCurl || !curl) + return; + + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + + string dest = localBnpName(url); +#ifdef LOG_DL + nlwarning("add to download '%s' dest '%s'", url.c_str(), dest.c_str()); +#endif + // create the local file + if (NLMISC::CFile::fileExists(dest)) + { + if (action == "override" || action == "delete") + { + CFile::setRWAccess(dest); + NLMISC::CFile::deleteFile(dest.c_str()); + } + } + if (action != "delete") + { + FILE *fp = fopen (dest.c_str(), "wb"); + if (fp == NULL) + { + nlwarning("Can't open file '%s' for writing: code=%d '%s'", dest.c_str (), errno, strerror(errno)); + return; + } + curl_easy_setopt(curl, CURLOPT_FILE, fp); + + curl_multi_add_handle(MultiCurl, curl); + Curls.push_back(CDataDownload(curl, url, fp, BnpType, NULL)); +#ifdef LOG_DL + nlwarning("adding handle %x, %d curls", curl, Curls.size()); +#endif + RunningCurls++; + } +} + +void CGroupHTML::initBnpDownload() +{ +#ifdef LOG_DL + nlwarning("Init Bnp Download"); +#endif + string pathName = "user"; + if ( ! CFile::isExists( pathName ) ) + CFile::createDirectory( pathName ); +} + +// Call this evenly to check if an element is downloaded and then manage it +void CGroupHTML::checkDownloads() { //nlassert(_CrtCheckMemory()); @@ -142,7 +237,7 @@ void CGroupHTML::checkImageDownload() { if (msg->msg == CURLMSG_DONE) { - for (vector::iterator it=Curls.begin(); it::iterator it=Curls.begin(); iteasy_handle == it->curl) { @@ -156,58 +251,87 @@ void CGroupHTML::checkImageDownload() #endif curl_easy_cleanup(it->curl); - string image = localImageName(it->url); + string file; - if(CURLE_OK != res || r < 200 || r >= 300) + if (it->type == ImgType) + file = localImageName(it->url)+".tmp"; + else + file = localBnpName(it->url); + + if(res != CURLE_OK || r < 200 || r >= 300) { - NLMISC::CFile::deleteFile((image+".tmp").c_str()); + NLMISC::CFile::deleteFile(file.c_str()); } else { string finalUrl; - CFile::moveFile(image.c_str(), (image+".tmp").c_str()); - if (lookupLocalFile (finalUrl, image.c_str(), false)) + if (it->type == ImgType) { - for(uint i = 0; i < it->imgs.size(); i++) + string image = localImageName(it->url); + CFile::moveFile(image.c_str(), (image+".tmp").c_str()); + if (lookupLocalFile (finalUrl, image.c_str(), false)) { - // don't display image that are not power of 2 - uint32 w, h; - CBitmap::loadSize (image, w, h); - if (w == 0 || h == 0 || ((!NLMISC::isPowerOf2(w) || !NLMISC::isPowerOf2(h)) && !NL3D::CTextureFile::supportNonPowerOfTwoTextures())) - image.clear(); + for(uint i = 0; i < it->imgs.size(); i++) + { + // don't display image that are not power of 2 + uint32 w, h; + CBitmap::loadSize (image, w, h); + if (w == 0 || h == 0 || ((!NLMISC::isPowerOf2(w) || !NLMISC::isPowerOf2(h)) && !NL3D::CTextureFile::supportNonPowerOfTwoTextures())) + image.clear(); - CCtrlButton *btn = dynamic_cast(it->imgs[i]); - if(btn) - { -#ifdef LOG_DL - nlwarning("refresh new downloading image %d button %p", i, it->imgs[i]); -#endif - btn->setTexture (image); - btn->setTexturePushed(image); - btn->invalidateCoords(); - btn->invalidateContent(); - btn->resetInvalidCoords(); - btn->updateCoords(); - paragraphChange(); - } - else - { - CViewBitmap *btm = dynamic_cast(it->imgs[i]); - if(btm) + CCtrlButton *btn = dynamic_cast(it->imgs[i]); + if(btn) { -#ifdef LOG_DL - nlwarning("refresh new downloading image %d image %p", i, it->imgs[i]); -#endif - btm->setTexture (image); - btm->invalidateCoords(); - btm->invalidateContent(); - btm->resetInvalidCoords(); - btm->updateCoords(); + #ifdef LOG_DL + nlwarning("refresh new downloading image %d button %p", i, it->imgs[i]); + #endif + btn->setTexture (image); + btn->setTexturePushed(image); + btn->invalidateCoords(); + btn->invalidateContent(); + btn->resetInvalidCoords(); + btn->updateCoords(); paragraphChange(); } + else + { + CViewBitmap *btm = dynamic_cast(it->imgs[i]); + if(btm) + { + #ifdef LOG_DL + nlwarning("refresh new downloading image %d image %p", i, it->imgs[i]); + #endif + btm->setTexture (image); + btm->invalidateCoords(); + btm->invalidateContent(); + btm->resetInvalidCoords(); + btm->updateCoords(); + paragraphChange(); + } + } } } } + else + { + if (lookupLocalFile (finalUrl, file.c_str(), false)) + { + nlinfo("BNPCHECK : downloaded"); + + bool memoryCompressed = CPath::isMemoryCompressed(); + if (memoryCompressed) + { + CPath::memoryUncompress(); + } + CPath::addSearchPath("user/", true, false, NULL); + if (memoryCompressed) + { + CPath::memoryCompress(); + } + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + pIM->executeLuaScript("game:onBnpDownloadFinish()", true); + } + } } Curls.erase(it); @@ -220,32 +344,11 @@ void CGroupHTML::checkImageDownload() RunningCurls = NewRunningCurls; } -void CGroupHTML::initImageDownload() + +void CGroupHTML::releaseDownloads() { #ifdef LOG_DL - nlwarning("Init Image Download"); -#endif - MultiCurl = curl_multi_init(); - RunningCurls = 0; -/* -// Get current flag -int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); -// Turn on leak-checking bit -tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF; -// Set flag to the new value -_CrtSetDbgFlag( tmpFlag ); -*/ - - - string pathName = "cache"; - if ( ! CFile::isExists( pathName ) ) - CFile::createDirectory( pathName ); -} - -void CGroupHTML::releaseImageDownload() -{ -#ifdef LOG_DL - nlwarning("Release Image Download"); + nlwarning("Release Downloads"); #endif if(MultiCurl) curl_multi_cleanup(MultiCurl); @@ -1251,6 +1354,21 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c endParagraph(); _UL.push_back(true); break; + case HTML_OBJECT: + _ObjectType = ""; + _ObjectData = ""; + _ObjectMD5Sum = ""; + _ObjectAction = ""; + if (present[HTML_OBJECT_TYPE] && value[HTML_OBJECT_TYPE]) + _ObjectType = value[HTML_OBJECT_TYPE]; + if (present[HTML_OBJECT_DATA] && value[HTML_OBJECT_DATA]) + _ObjectData = value[HTML_OBJECT_DATA]; + if (present[HTML_OBJECT_ID] && value[HTML_OBJECT_ID]) + _ObjectMD5Sum = value[HTML_OBJECT_ID]; + if (present[HTML_OBJECT_STANDBY] && value[HTML_OBJECT_STANDBY]) + _ObjectAction = value[HTML_OBJECT_STANDBY]; + + break; } } } @@ -1371,6 +1489,14 @@ void CGroupHTML::endElement (uint element_number) popIfNotEmpty (_UL); } break; + case HTML_OBJECT: + if (_ObjectType=="application/ryzom-data") + { + if (!_ObjectData.empty()) + { + addBnpDownload(_ObjectData, _ObjectAction); + } + } } } } @@ -1483,7 +1609,11 @@ CGroupHTML::CGroupHTML(const TCtorParam ¶m) DefaultBackgroundBitmapView = "bg"; clearContext(); + MultiCurl = curl_multi_init(); + RunningCurls = 0; + initImageDownload(); + initBnpDownload(); } // *************************************************************************** @@ -2313,9 +2443,14 @@ CCtrlButton *CGroupHTML::addButton(CCtrlButton::EType type, const std::string &/ ctrlButton->setActionOnLeftClick (actionHandler); ctrlButton->setParamsOnLeftClick (actionHandlerParams); - // Translate the tooltip + // Translate the tooltip or display raw text (tooltip from webig) if (tooltip) - ctrlButton->setDefaultContextHelp (CI18N::get (tooltip)); + { + if (CI18N::hasTranslation(tooltip)) + ctrlButton->setDefaultContextHelp (CI18N::get(tooltip)); + else + ctrlButton->setDefaultContextHelp (ucstring(tooltip)); + } getParagraph()->addChild (ctrlButton); paragraphChange (); @@ -2913,7 +3048,7 @@ void CGroupHTML::handle () void CGroupHTML::draw () { - checkImageDownload(); + checkDownloads(); CGroupScrollText::draw (); } diff --git a/code/ryzom/client/src/interface_v3/group_html.h b/code/ryzom/client/src/interface_v3/group_html.h index daf0792e9..1d8fd9229 100644 --- a/code/ryzom/client/src/interface_v3/group_html.h +++ b/code/ryzom/client/src/interface_v3/group_html.h @@ -293,6 +293,8 @@ protected : // the script to execute std::string _LuaScript; + bool _ParsingBnpUrl; + std::string _BnpUrl; // Someone is conecting. We got problem with libwww : 2 connection requests can deadlock the client. static CGroupHTML *_ConnectingLock; @@ -481,6 +483,12 @@ protected : bool _SelectOption; ucstring _SelectOptionStr; + // Current node is a object + std::string _ObjectType; + std::string _ObjectData; + std::string _ObjectMD5Sum; + std::string _ObjectAction; + // Get last char ucchar getLastChar() const; @@ -527,25 +535,38 @@ private: static ucstring decodeHTMLEntities(const ucstring &str); // ImageDownload system + enum TDataType {ImgType= 0, BnpType}; - struct CImageDownload + struct CDataDownload { - CImageDownload(CURL *c, const std::string &u, FILE *f, CViewBase *i) : curl(c), url(u), fp(f) { imgs.push_back(i); } + CDataDownload(CURL *c, const std::string &u, FILE *f, TDataType t, CViewBase *i) : curl(c), url(u), fp(f), type(t) { imgs.push_back(i); } CURL *curl; std::string url; + TDataType type; FILE *fp; std::vector imgs; }; - std::vector Curls; + std::vector Curls; CURLM *MultiCurl; int RunningCurls; void initImageDownload(); - void releaseImageDownload(); void checkImageDownload(); void addImageDownload(const std::string &url, CViewBase *img); std::string localImageName(const std::string &url); + + + + // BnpDownload system + void initBnpDownload(); + void checkBnpDownload(); + void addBnpDownload(const std::string &url, const std::string &action); + std::string localBnpName(const std::string &url); + + void releaseDownloads(); + void checkDownloads(); + }; // adapter group that store y offset for inputs inside an html form diff --git a/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp b/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp index cfce12775..a5dae02f3 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp +++ b/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp @@ -514,6 +514,13 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) if (!isForageSource) { + CViewBase * invisibleLogo = info->getView("invisible_logo"); + if (entity->isUser() && invisibleLogo) + { + bool invisible = pIM->getDbProp("SERVER:USER:IS_INVISIBLE")->getValueBool(); + invisibleLogo->setActive(invisible); + } + // Get the target bitmap CViewBase *target = leftGroup->getView ("target"); if (target) @@ -532,25 +539,91 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) info->_MissionTarget = bitmap; } - // set or inactive pvp logos + CViewBase * pvpCivLogo = info->getView ("pvp_faction_civ_logo"); + CViewBase * pvpCultLogo = info->getView ("pvp_faction_cult_logo"); + CPlayerCL * pPlayer = dynamic_cast(entity); - if (pPlayer != NULL && needPvPLogo) + if (pPlayer == NULL) + needPvPLogo = false; + + // set or inactive pvp logos + bool needCivPvpLogo = needPvPLogo; + bool needCultPvpLogo = needPvPLogo; + + if (pPlayer != NULL && needPvPLogo && pvpCivLogo && pvpCultLogo) { - CViewBase * pvpFactionLogo = info->getView ("pvp_faction_logo"); - if (pvpFactionLogo) + uint8 civToDisplay = (uint8)(pPlayer->getClanCivMaxFame() & 0xFF); + uint8 cultToDisplay = (uint8)(pPlayer->getClanCultMaxFame() & 0xFF); + + if (!entity->isUser()) { - if( pPlayer->getPvpMode()&PVP_MODE::PvpFaction || pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged) - { - CViewBitmap * pvpFactionLogoBmp = dynamic_cast(pvpFactionLogo); - if( pvpFactionLogoBmp ) - pvpFactionLogoBmp->setTexture( pIM->getDefine(PVP_CLAN::toIconDefineString(pPlayer->getPvpClan())) ); - } - else - { - pvpFactionLogo->setActive(false); - } + // Check if are Civ Allies + for (uint8 i = 0; i < 4; i++) + if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpAlly(i)) || + (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) ) + civToDisplay = i; + + // Check if are Civ Ennemies + for (uint8 i = 0; i < 4; i++) + if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpEnnemy(i)) || + (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) ) + civToDisplay = i; + + // Check if are Cult Allies + for (uint8 i = 4; i < 7; i++) + if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpAlly(i)) || + (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) ) + cultToDisplay = i; + + // Check if are Cult Ennemies + for (uint8 i = 4; i < 7; i++) + if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpEnnemy(i)) || + (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) ) + cultToDisplay = i; } + if ((pPlayer->getPvpMode() & PVP_MODE::PvpFaction) || (pPlayer->getPvpMode() & PVP_MODE::PvpFactionFlagged)) + { + CViewBitmap * pvpCivLogoBmp = dynamic_cast(pvpCivLogo); + if( pvpCivLogoBmp ) + { + if (pPlayer->isPvpAlly(civToDisplay)) + if (pPlayer->isPvpRanger()) + pvpCivLogoBmp->setTexture("pvp_ally_ranger.tga"); + else + pvpCivLogoBmp->setTexture("pvp_ally_"+toString(civToDisplay)+".tga"); + else if (pPlayer->isPvpEnnemy(civToDisplay)) + if (pPlayer->isPvpMarauder()) + pvpCivLogoBmp->setTexture("pvp_enemy_marauder.tga"); + else + pvpCivLogoBmp->setTexture("pvp_enemy_"+toString(civToDisplay)+".tga"); + else + needCivPvpLogo = false; + } + + CViewBitmap * pvpCultLogoBmp = dynamic_cast(pvpCultLogo); + if( pvpCultLogoBmp ) + { + if (pPlayer->isPvpAlly(cultToDisplay)) + if (pPlayer->isPvpPrimas()) + pvpCultLogoBmp->setTexture("pvp_ally_primas.tga"); + else + pvpCultLogoBmp->setTexture("pvp_ally_"+toString(cultToDisplay)+".tga"); + else if (pPlayer->isPvpEnnemy(cultToDisplay)) + if (pPlayer->isPvpTrytonist()) + pvpCultLogoBmp->setTexture("pvp_enemy_trytonist.tga"); + else + pvpCultLogoBmp->setTexture("pvp_enemy_"+toString(cultToDisplay)+".tga"); + else + needCultPvpLogo = false; + } + } + else + { + needCivPvpLogo = false; + needCultPvpLogo = false; + } + CViewBase * pvpOutpostLogo = info->getView ("pvp_outpost_logo"); if (pvpOutpostLogo) { @@ -563,7 +636,7 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) CViewBase * pvpDuelLogo = info->getView ("pvp_duel_logo"); if (pvpDuelLogo) { - if( !(pPlayer->getPvpMode()&PVP_MODE::PvpDuel) ) + if( !(pPlayer->getPvpMode()&PVP_MODE::PvpDuel || pPlayer->getPvpMode()&PVP_MODE::PvpChallenge) ) { pvpDuelLogo->setActive(false); } @@ -571,11 +644,6 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) } else { - // unactive pvp logos - CViewBase * pvpFactionLogo = info->getView ("pvp_faction_logo"); - if (pvpFactionLogo) - pvpFactionLogo->setActive(false); - CViewBase * pvpOutpostLogo = info->getView ("pvp_outpost_logo"); if (pvpOutpostLogo) pvpOutpostLogo->setActive(false); @@ -584,6 +652,13 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) if (pvpDuelLogo) pvpDuelLogo->setActive(false); } + + if (pvpCivLogo) + pvpCivLogo->setActive(needCivPvpLogo); + + if (pvpCultLogo) + pvpCultLogo->setActive(needCultPvpLogo); + } // No bar and no string ? diff --git a/code/ryzom/client/src/interface_v3/guild_manager.cpp b/code/ryzom/client/src/interface_v3/guild_manager.cpp index 2be4808f0..8ecf90a96 100644 --- a/code/ryzom/client/src/interface_v3/guild_manager.cpp +++ b/code/ryzom/client/src/interface_v3/guild_manager.cpp @@ -47,6 +47,7 @@ using namespace std; using namespace NLMISC; +extern CPeopleInterraction PeopleInterraction; NLMISC_REGISTER_OBJECT(CViewBase, CDBGroupListAscensor, std::string, "list_sheet_guild"); @@ -54,26 +55,27 @@ NLMISC_REGISTER_OBJECT(CViewBase, CDBGroupListAscensor, std::string, "list_sheet // Interface part // *************************************************************************** -#define WIN_GUILD "ui:interface:guild" -#define WIN_GUILD_CHAT "ui:interface:guild_chat" -#define WIN_GUILD_FORUM "ui:interface:guild_forum" -#define VIEW_TEXT_GUILD_QUIT "ui:interface:guild:content:tab_guild:quit_guild" -#define CTRL_SHEET_GUILD_BLASON "ui:interface:guild:content:tab_guild:blason" -#define VIEW_TEXT_GUILD_MEMBER_COUNT "ui:interface:guild:content:tab_guild:member_count" +#define WIN_GUILD "ui:interface:guild" +#define WIN_GUILD_CHAT "ui:interface:guild_chat" +#define WIN_GUILD_FORUM "ui:interface:guild_forum" +#define VIEW_TEXT_GUILD_QUIT "ui:interface:guild:content:tab_guild:quit_guild" +#define CTRL_SHEET_GUILD_BLASON "ui:interface:guild:content:tab_guild:blason" +#define VIEW_TEXT_GUILD_MEMBER_COUNT "ui:interface:guild:content:tab_guild:member_count" -#define LIST_GUILD_MEMBERS "ui:interface:guild:content:tab_guild:list_member:guild_members" -#define CTRL_QUIT_GUILD "ui:interface:guild:content:tab_guild:quit_guild" -#define TEMPLATE_GUILD_MEMBER "member_template" -#define TEMPLATE_GUILD_MEMBER_NAME "name" -#define TEMPLATE_GUILD_MEMBER_GRADE "grade" -#define TEMPLATE_GUILD_MEMBER_SCORE "score" -#define MENU_GUILD_MEMBER "ui:interface:menu_member" +#define LIST_GUILD_MEMBERS "ui:interface:guild:content:tab_guild:list_member:guild_members" +#define CTRL_QUIT_GUILD "ui:interface:guild:content:tab_guild:quit_guild" +#define TEMPLATE_GUILD_MEMBER "member_template" +#define TEMPLATE_GUILD_MEMBER_NAME "name" +#define TEMPLATE_GUILD_MEMBER_GRADE "grade" +#define TEMPLATE_GUILD_MEMBER_SCORE "score" +#define TEMPLATE_GUILD_MEMBER_ENTER_DATE "enter_date" +#define MENU_GUILD_MEMBER "ui:interface:menu_member" -#define WIN_ASCENSOR "ui:interface:ascensor_teleport_list" +#define WIN_ASCENSOR "ui:interface:ascensor_teleport_list" -#define WIN_JOIN_PROPOSAL "ui:interface:join_guild_proposal" -#define VIEW_JOIN_PROPOSAL_PHRASE "ui:interface:join_guild_proposal:content:inside:phrase" +#define WIN_JOIN_PROPOSAL "ui:interface:join_guild_proposal" +#define VIEW_JOIN_PROPOSAL_PHRASE "ui:interface:join_guild_proposal:content:inside:phrase" CGuildManager* CGuildManager::_Instance = NULL; @@ -109,16 +111,53 @@ static inline bool lt_member_grade(const SGuildMember &m1, const SGuildMember &m return m1.Grade < m2.Grade; } +static inline bool lt_member_online(const SGuildMember &m1, const SGuildMember &m2) +{ + if (m1.Online == m2.Online) + { + return lt_member_grade(m1, m2); + } + + // Compare online status + switch (m1.Online) + { + case ccs_online: + // m1 is < if m1 is online + return true; + break; + case ccs_online_abroad: + // m1 is < if m2 is offline + return (m2.Online == ccs_offline); + break; + case ccs_offline: + default: + // m2 is always < if m1 is offline + return false; + break; + } +} + + // *************************************************************************** -void CGuildManager::sortGuildMembers() +void CGuildManager::sortGuildMembers(TSortOrder order) { if (_GuildMembers.size() < 2) return; - // First sort by name - sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_name); - - // Second sort by grade - stable_sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_grade); + switch (order) + { + default: + case sort_grade: + sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_name); + stable_sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_grade); + break; + case sort_name: + sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_name); + break; + case sort_online: + sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_name); + stable_sort(_GuildMembers.begin(), _GuildMembers.end(), lt_member_online); + break; + } } bool CGuildManager::isProxy() @@ -715,8 +754,9 @@ class CAHGuildSheetOpen : public IActionHandler // *** Update Members, if necessary if(updateMembers) { + CGuildManager::TSortOrder order = (CGuildManager::TSortOrder)(pIM->getDbProp("UI:SAVE:GUILD_LIST:SORT_ORDER")->getValue32()); // Sort the members in Guild Manager - pGM->sortGuildMembers(); + pGM->sortGuildMembers(order); // update member count view const vector &rGuildMembers = pGM->getGuildMembers(); @@ -784,6 +824,21 @@ class CAHGuildSheetOpen : public IActionHandler } } + // Enter Date + CViewText *pViewEnterDate = dynamic_cast(pLine->getView(TEMPLATE_GUILD_MEMBER_ENTER_DATE)); + if (pViewEnterDate != NULL) + { + CRyzomTime rt; + rt.updateRyzomClock(rGuildMembers[i].EnterDate); + ucstring str = toString("%04d", rt.getRyzomYear()) + " "; + str += CI18N::get("uiJenaYear") + " : "; + str += CI18N::get("uiAtysianCycle") + " "; + str += toString("%01d", rt.getRyzomCycle()+1) +", "; + str += CI18N::get("ui"+MONTH::toString( (MONTH::EMonth)rt.getRyzomMonthInCurrentCycle() )) + ", "; + str += toString("%02d", rt.getRyzomDayOfMonth()+1); + pViewEnterDate->setText(str); + } + // Add to the list pLine->setParent (pParent); pParent->addChild (pLine); @@ -852,15 +907,7 @@ class CAHGuildSheetMenuOpen : public IActionHandler return; } - // *** Check with the grade of the local player wich types of actions we can do on the player selected - // Grade less or equal cant do anything - if (pGM->getGrade() >= rGuildMembers[nLineNb].Grade) - { - // Close - pIM->disableModalWindow(); - return; - } // enable or disable menu entries if (pGM->isProxy()) @@ -871,7 +918,11 @@ class CAHGuildSheetMenuOpen : public IActionHandler else { // Depending on the grade we can do things or other - if (pGM->getGrade() == EGSPD::CGuildGrade::Leader) + + // Grade less or equal can't do anything + if (pGM->getGrade() >= rGuildMembers[nLineNb].Grade) + setRights(false, false, false, false, false, false, false); + else if (pGM->getGrade() == EGSPD::CGuildGrade::Leader) setRights(true, true, true, true, true, true, true); else if (pGM->getGrade() == EGSPD::CGuildGrade::HighOfficer) setRights(false, false, true, true, true, true, true); @@ -928,6 +979,70 @@ static void sendMsgSetGrade(EGSPD::CGuildGrade::TGuildGrade Grade) } } +// *************************************************************************** +// Sort the guild member list +class CAHGuildSheetSortGuildList : public IActionHandler +{ +public: + void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) + { + CInterfaceManager* pIM= CInterfaceManager::getInstance(); + CGuildManager::TSortOrder order = (CGuildManager::TSortOrder)(pIM->getDbProp("UI:SAVE:GUILD_LIST:SORT_ORDER")->getValue32()); + + order = (CGuildManager::TSortOrder)(order + 1); + if (order == CGuildManager::END_SORT_ORDER) + { + order = CGuildManager::START_SORT_ORDER; + } + + pIM->getDbProp("UI:SAVE:GUILD_LIST:SORT_ORDER")->setValue32((sint32)order); + pIM->runActionHandler("guild_sheet_open", NULL, toString("update_members=1")); + } +}; +REGISTER_ACTION_HANDLER(CAHGuildSheetSortGuildList, "sort_guild_list"); + +// *************************************************************************** +// Invoke the 'tell' command on a contact from its menu +// The tell command is displayed in the 'around me' window +class CAHGuildSheetTellMember : public IActionHandler +{ +public: + void execute (CCtrlBase * pCaller, const std::string &/* sParams */) + { + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + CGuildManager *pGM = CGuildManager::getInstance(); + const vector &rGuildMembers = pGM->getGuildMembers(); + // *** Check and retrieve the current member index (index in the member list) + CCtrlBase *ctrlLaunchingModal= pIM->getCtrlLaunchingModal(); + if (pCaller == NULL) + { + // Error -> Close + return; + } + string sId = pCaller->getId(); + sId = sId.substr(sId.rfind('m')+1,sId.size()); + sint32 nLineNb; + fromString(sId, nLineNb); + if ((nLineNb < 0) || (nLineNb >= (sint32)rGuildMembers.size())) + { + // Error -> Close + return; + } + MemberIndexSelected= nLineNb; + MemberNameSelected = rGuildMembers[nLineNb].Name; + + CPeopleInterraction::displayTellInMainChat(MemberNameSelected); + } + + // Current selection + static sint32 MemberIndexSelected; // Index of the member selected when left clicked + static ucstring MemberNameSelected; // Name of the member selected when lef clicked +}; +REGISTER_ACTION_HANDLER(CAHGuildSheetTellMember, "guild_tell_member"); + +sint32 CAHGuildSheetTellMember::MemberIndexSelected= -1; +ucstring CAHGuildSheetTellMember::MemberNameSelected; + // *************************************************************************** class CAHGuildSheetSetLeader : public IActionHandler { diff --git a/code/ryzom/client/src/interface_v3/guild_manager.h b/code/ryzom/client/src/interface_v3/guild_manager.h index db5cbc0ec..17b21c74c 100644 --- a/code/ryzom/client/src/interface_v3/guild_manager.h +++ b/code/ryzom/client/src/interface_v3/guild_manager.h @@ -98,7 +98,16 @@ public: const SGuild &getGuild() { return _Guild; } const std::vector &getGuildMembers() { return _GuildMembers; } - void sortGuildMembers(); + enum TSortOrder + { + sort_grade, + START_SORT_ORDER = sort_grade, + sort_name, + sort_online, + END_SORT_ORDER + }; + + void sortGuildMembers(TSortOrder order = sort_grade); /// Check if the guild is a proxified guild (not managed on the actual shard) bool isProxy(); diff --git a/code/ryzom/client/src/interface_v3/interface_3d_scene.cpp b/code/ryzom/client/src/interface_v3/interface_3d_scene.cpp index dd6047b1a..324edf91a 100644 --- a/code/ryzom/client/src/interface_v3/interface_3d_scene.cpp +++ b/code/ryzom/client/src/interface_v3/interface_3d_scene.cpp @@ -506,7 +506,10 @@ void CInterface3DScene::draw () for (i = 0; i < _Characters.size(); ++i) _Characters[i]->setClusterSystem ((UInstanceGroup*)-1); for (i = 0; i < _Shapes.size(); ++i) - _Shapes[i]->getShape().setClusterSystem ((UInstanceGroup*)-1); + { + if (!_Shapes[i]->getShape().empty()) + _Shapes[i]->getShape().setClusterSystem ((UInstanceGroup*)-1); + } for (i = 0; i < _FXs.size(); ++i) if (!_FXs[i]->getPS().empty()) _FXs[i]->getPS().setClusterSystem ((UInstanceGroup*)-1); @@ -1344,7 +1347,20 @@ std::string CInterface3DShape::getName() const // ---------------------------------------------------------------------------- void CInterface3DShape::setName (const std::string &ht) { - string lwrname = strlwr(ht); + if (ht.empty()) + { + CInterface3DScene *pI3DS = dynamic_cast(_Parent); + nlassert(pI3DS != NULL); + + if (!_Instance.empty()) + { + pI3DS->getScene()->deleteInstance(_Instance); + } + return; + _Name.clear(); + } + + string lwrname = toLower(ht); if (lwrname != _Name) { CInterface3DScene *pI3DS = dynamic_cast(_Parent); diff --git a/code/ryzom/client/src/interface_v3/interface_manager.cpp b/code/ryzom/client/src/interface_v3/interface_manager.cpp index 66610dbf2..65a59a6b4 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.cpp +++ b/code/ryzom/client/src/interface_v3/interface_manager.cpp @@ -101,6 +101,7 @@ #include "../entity_animation_manager.h" // for emotes #include "../net_manager.h" // for emotes #include "../client_chat_manager.h" // for emotes +#include "../entities.h" #include "chat_text_manager.h" #include "../npc_icon.h" @@ -1288,6 +1289,18 @@ void CInterfaceManager::updateFrameEvents() pVT = dynamic_cast(getElementFromId("ui:interface:map:content:map_content:time")); if (pVT != NULL) pVT->setText(str); + + str.clear(); + // Update the clock in the compass if enabled. + pVT = dynamic_cast(getElementFromId("ui:interface:compass:clock:time")); + if (pVT != NULL) + { + if (pVT->getActive()) + { + str = getTimestampHuman("%H:%M"); + pVT->setText(str); + } + } } } @@ -6274,3 +6287,300 @@ void CInterfaceManager::CServerToLocalAutoCopy::onLocalChange(ICDBNode *localNod } } +// ------------------------------------------------------------------------------------------------ +char* CInterfaceManager::getTimestampHuman(const char* format /* "[%H:%M:%S] " */) +{ + static char cstime[25]; + time_t date; + time (&date); + struct tm *tms = localtime(&date); + if (tms) + { + strftime(cstime, 25, format, tms); + } + else + { + strcpy(cstime, ""); + } + + return cstime; +} + + +/* + * Parse tokens in a chatmessage or emote + * + * Valid subjects: + * $me$ + * $t$ + * $tt$ + * $tm1$..$tm8$ + * + * Valid parameters: + * $.name$ + * $.title$ + * $.race$ + * $.guild$ + * $.gs(m/f/n)$ + * + * Default parameter if parameter result is empty: + * $./$ + * + * All \d's in default parameter remove a following character. + */ +bool CInterfaceManager::parseTokens(ucstring& ucstr) +{ + ucstring str = ucstr; + ucstring start_token = "$"; + ucstring end_token = "$"; + size_t start_pos = 0; + size_t end_pos = 1; + + sint endless_loop_protector = 0; + while ((start_pos < str.length() - 1) && + ((start_pos = str.find(start_token, start_pos)) != string::npos)) + { + endless_loop_protector++; + if (endless_loop_protector > 100) + { + break; + } + + // Get the whole token substring first + end_pos = str.find(end_token, start_pos + 1); + + if ((start_pos == string::npos) || + (end_pos == string::npos) || + (end_pos <= start_pos + 1)) + { + // Wrong formatting; give up on this one. + start_pos = max(start_pos, end_pos); + continue; + } + + // Get everything between the two "$" + size_t token_start_pos = start_pos + start_token.length(); + size_t token_end_pos = end_pos - end_token.length(); + if ((token_start_pos - token_end_pos) < 0) + { + // Wrong formatting; give up on this one. + start_pos = end_pos; + continue; + } + + ucstring token_whole = str.luabind_substr(start_pos, end_pos - start_pos + 1); + ucstring token_string = token_whole.luabind_substr(1, token_whole.length() - 2); + ucstring token_replacement = token_whole; + ucstring token_default = token_whole; + + ucstring token_subject; + ucstring token_param; + + // Does the token have a parameter? + // If not it is 'name' by default + vector token_vector; + vector param_vector; + splitUCString(token_string, ucstring("."), token_vector); + token_subject = token_vector[0]; + if (token_vector.size() == 1) + { + splitUCString(token_subject, ucstring("/"), param_vector); + token_subject = param_vector[0]; + token_param = ucstring("name"); + } + else + { + token_param = token_vector[1]; + if (token_param.luabind_substr(0, 3) != ucstring("gs(")) + { + splitUCString(token_vector[1], ucstring("/"), param_vector); + token_param = param_vector[0]; + } + } + + // Get any default value, if not gs + sint extra_replacement = 0; + if (token_param.luabind_substr(0, 3) != ucstring("gs(")) + { + if (param_vector.size() == 2) + { + // Set default value + token_replacement = param_vector[1]; + // Delete following chars for every '\d' in default + string::size_type token_replacement_pos; + while ((token_replacement_pos = token_replacement.find(ucstring("\\d"))) != string::npos) + { + token_replacement.replace(token_replacement_pos, 2, ucstring("")); + extra_replacement++; + } + token_default = token_replacement; + } + } + + CEntityCL *pTokenSubjectEntity = NULL; + + if (token_subject == ucstring("me")) + { + pTokenSubjectEntity = static_cast(UserEntity); + } + else if (token_subject == ucstring("t")) + { + // Target + uint targetSlot = UserEntity->targetSlot(); + pTokenSubjectEntity = EntitiesMngr.entity(targetSlot); + } + else if (token_subject == ucstring("tt")) + { + // Target's target + uint targetSlot = UserEntity->targetSlot(); + CEntityCL *target = EntitiesMngr.entity(targetSlot); + + if (target) + { + // Check the new slot. + CLFECOMMON::TCLEntityId newSlot = target->targetSlot(); + CEntityCL* pE = EntitiesMngr.entity(newSlot); + if (pE) + { + pTokenSubjectEntity = pE; + } + } + } + else if ((token_subject.length() == 3) && + (token_subject.luabind_substr(0, 2) == ucstring("tm"))) + { + // Teammate + uint indexInTeam = 0; + fromString(token_subject.luabind_substr(2, 1).toString(), indexInTeam); + + // Make 0-based + --indexInTeam; + if (indexInTeam < PeopleInterraction.TeamList.getNumPeople() ) + { + // Index is the database index (serverIndex() not used for team list) + CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp( NLMISC::toString(TEAM_DB_PATH ":%hu:NAME", indexInTeam ), false); + if (pNL && pNL->getValueBool() ) + { + // There is a character corresponding to this index + pNL = CInterfaceManager::getInstance()->getDbProp( NLMISC::toString( TEAM_DB_PATH ":%hu:UID", indexInTeam ), false ); + if (pNL) + { + CLFECOMMON::TClientDataSetIndex compressedIndex = pNL->getValue32(); + + // Search entity in vision + CEntityCL *entity = EntitiesMngr.getEntityByCompressedIndex( compressedIndex ); + if (entity) + { + pTokenSubjectEntity = entity; + } + } + } + } + } + else + { + // Unknown token subject, skip it + start_pos = end_pos; + continue; + } + + if (pTokenSubjectEntity != NULL) + { + // Parse the parameter + if (token_param == ucstring("name")) + { + ucstring name = pTokenSubjectEntity->getDisplayName(); + // special case where there is only a title, very rare case for some NPC + if (name.empty()) + { + name = pTokenSubjectEntity->getTitle(); + } + token_replacement = name.empty() ? token_replacement : name; + } + else if (token_param == ucstring("title")) + { + ucstring title = pTokenSubjectEntity->getTitle(); + token_replacement = title.empty() ? token_replacement : title; + } + else if (token_param == ucstring("race")) + { + CCharacterCL *pC = (CCharacterCL*)(pTokenSubjectEntity); + if (pC) + { + EGSPD::CPeople::TPeople race = pC->people(); + if (race >= EGSPD::CPeople::Playable && race <= EGSPD::CPeople::EndPlayable) + { + ucstring srace = NLMISC::CI18N::get("io" + EGSPD::CPeople::toString(race)); + token_replacement = srace.empty() ? token_replacement : srace; + } + } + } + else if (token_param == ucstring("guild")) + { + STRING_MANAGER::CStringManagerClient *pSMC = STRING_MANAGER::CStringManagerClient::instance(); + ucstring ucGuildName; + if (pSMC->getString(pTokenSubjectEntity->getGuildNameID(), ucGuildName)) + { + token_replacement = ucGuildName.empty() ? token_replacement : ucGuildName; + } + } + else if (token_param.luabind_substr(0, 3) == ucstring("gs(") && + token_param.luabind_substr(token_param.length() - 1 , 1) == ucstring(")")) + { + // Gender string + vector strList; + ucstring gender_string = token_param.luabind_substr(3, token_param.length() - 4); + splitUCString(gender_string, ucstring("/"), strList); + + if (strList.size() <= 1) + { + start_pos = end_pos; + continue; + } + + // Only care about gender if it's a humanoid. + GSGENDER::EGender gender = GSGENDER::neutral; + if (pTokenSubjectEntity->isUser() || pTokenSubjectEntity->isPlayer() || pTokenSubjectEntity->isNPC()) + { + CCharacterCL *pC = (CCharacterCL*)(pTokenSubjectEntity); + if (pC) + { + gender = pC->getGender(); + } + } + + // Neuter part is optional. + // Fallback to male if something is wrong. + GSGENDER::EGender g = ((uint)gender >= strList.size()) ? GSGENDER::male : gender; + token_replacement = strList[g]; + } + } + + if (token_whole == token_replacement) + { + // Nothing to replace; show message and exit + CInterfaceManager *im = CInterfaceManager::getInstance(); + ucstring message = ucstring(CI18N::get("uiUntranslatedToken")); + message.replace(message.find(ucstring("%s")), 2, token_whole); + im->displaySystemInfo(message); + return false; + } + + + // Replace all occurances of token with the replacement + size_t token_whole_pos = str.find(token_whole); + start_pos = 0; + + // Only do extra replacement if using default + extra_replacement = (token_replacement == token_default) ? extra_replacement : 0; + while (str.find(token_whole, start_pos) != string::npos) + { + str = str.replace(token_whole_pos, token_whole.length() + extra_replacement, token_replacement); + start_pos = token_whole_pos + token_replacement.length(); + token_whole_pos = str.find(token_whole, start_pos); + } + } + + ucstr = str; + return true;; +} \ No newline at end of file diff --git a/code/ryzom/client/src/interface_v3/interface_manager.h b/code/ryzom/client/src/interface_v3/interface_manager.h index 9c22c57a1..cd3ad23ba 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.h +++ b/code/ryzom/client/src/interface_v3/interface_manager.h @@ -712,6 +712,14 @@ public: void updateTooltipCoords(); + /** Returns a human readable timestamp with the given format. + */ + static char* getTimestampHuman(const char* format = "[%H:%M:%S] "); + + /** Parses any tokens in the ucstring like $t$ or $g()$ + */ + static bool parseTokens(ucstring& ucstr); + // ------------------------------------------------------------------------------------------------ private: diff --git a/code/ryzom/client/src/interface_v3/interface_parser.cpp b/code/ryzom/client/src/interface_v3/interface_parser.cpp index 585b6cb58..f85992bb8 100644 --- a/code/ryzom/client/src/interface_v3/interface_parser.cpp +++ b/code/ryzom/client/src/interface_v3/interface_parser.cpp @@ -25,6 +25,7 @@ #include "nel/misc/algo.h" #include "nel/misc/mem_stream.h" #include "nel/misc/factory.h" +#include "nel/misc/big_file.h" #include "game_share/xml_auto_ptr.h" @@ -106,6 +107,7 @@ #include "../commands.h" #include "lua_helper.h" #include "lua_ihm.h" +#include "../r2/editor.h" #ifdef LUA_NEVRAX_VERSION #include "lua_ide_dll_nevrax/include/lua_ide_dll/ide_interface.h" // external debugger @@ -114,6 +116,23 @@ const uint32 UI_CACHE_SERIAL_CHECK = (uint32) 'IUG_'; using namespace NLMISC; +void badLuaParseMessageBox() +{ + NL3D::UDriver::TMessageBoxId ret = Driver->systemMessageBox( "LUA files reading failed!\n" + "Some LUA files are corrupted, moved or may have been removed.\n" + "Ryzom may need to be restarted to run properly.\n" + "Would you like to quit now?", + "LUA reading failed!", + NL3D::UDriver::yesNoType, + NL3D::UDriver::exclamationIcon); + if (ret == NL3D::UDriver::yesId) + { + extern void quitCrashReport (); + quitCrashReport (); + exit (EXIT_FAILURE); + } +} + void saveXMLTree(COFile &f, xmlNodePtr node) { // save node name @@ -494,10 +513,16 @@ static void interfaceScriptAsMemStream(const std::string &script, CMemStream &de } // ---------------------------------------------------------------------------- -bool CInterfaceParser::parseInterface (const std::vector & strings, bool reload, bool isFilename) +bool CInterfaceParser::parseInterface (const std::vector & strings, bool reload, bool isFilename, bool checkInData) { bool ok; + bool needCheck = checkInData; + +#if !FINAL_VERSION + needCheck = false; +#endif + // TestYoyo. UnHide For Parsing Profile /* NLMISC::CHTimer::startBench(); @@ -524,7 +549,19 @@ bool CInterfaceParser::parseInterface (const std::vector & strings, { //get the first file document pointer firstFileName = *it; - if (!file.open (CPath::lookup(firstFileName))) + string filename = CPath::lookup(firstFileName); + bool isInData = false; + if (filename.find ("@") != string::npos) + { + vector bigFilePaths; + CBigFile::getInstance().getBigFilePaths(bigFilePaths); + if (CBigFile::getInstance().getBigFileName(filename.substr(0, filename.find ("@"))) != "data/"+filename.substr(0, filename.find ("@"))) + isInData = false; + else + isInData = true; + } + + if ((needCheck && !isInData) || !file.open (CPath::lookup(firstFileName))) { // todo hulud interface syntax error nlwarning ("could not open file %s, skipping xml parsing",firstFileName.c_str()); @@ -929,7 +966,10 @@ bool CInterfaceParser::parseXMLDocument(xmlNodePtr root, bool reload) else if ( !strcmp((char*)root->name,"lua") ) { if(!parseLUAScript(root)) + { + badLuaParseMessageBox(); nlwarning ("could not parse 'lua'"); + } } root = root->next; @@ -4648,6 +4688,13 @@ void CInterfaceParser::uninitLUA() bool CInterfaceParser::loadLUA(const std::string &fileName, std::string &error) { // get file + + bool needCheck = true; + + #if !FINAL_VERSION + needCheck = false; + #endif + string pathName= CPath::lookup(fileName, false); if(pathName.empty()) { @@ -4655,6 +4702,20 @@ bool CInterfaceParser::loadLUA(const std::string &fileName, std::string &error) return false; } + bool isInData = false; + if (pathName.find ("@") != string::npos) + { + if (CBigFile::getInstance().getBigFileName(pathName.substr(0, pathName.find ("@"))) != "data/"+pathName.substr(0, pathName.find ("@"))) + isInData = false; + else + isInData = true; + } + + if (needCheck && !isInData) + { + return false; + } + // Parse script nlassert(_LuaState); try diff --git a/code/ryzom/client/src/interface_v3/interface_parser.h b/code/ryzom/client/src/interface_v3/interface_parser.h index 449a8f60d..753432ddc 100644 --- a/code/ryzom/client/src/interface_v3/interface_parser.h +++ b/code/ryzom/client/src/interface_v3/interface_parser.h @@ -103,7 +103,7 @@ public: * \param isFilename true if xmlFileNames array contains the names of the xml file, false, if each * array is a script itself */ - bool parseInterface (const std::vector &xmlFileNames, bool reload, bool isFilename = true); + bool parseInterface (const std::vector &xmlFileNames, bool reload, bool isFilename = true, bool checkInData = false); bool parseXMLDocument (xmlNodePtr root, bool reload); diff --git a/code/ryzom/client/src/interface_v3/lua_ihm.cpp b/code/ryzom/client/src/interface_v3/lua_ihm.cpp index 01186668e..2b9752b52 100644 --- a/code/ryzom/client/src/interface_v3/lua_ihm.cpp +++ b/code/ryzom/client/src/interface_v3/lua_ihm.cpp @@ -4287,7 +4287,14 @@ void CLuaIHM::tell(const ucstring &player, const ucstring &msg) { if (!msg.empty()) { - ChatMngr.tell(player.toUtf8(), msg); + // Parse any tokens in the message. + ucstring msg_modified = msg; + // Parse any tokens in the text + if ( ! CInterfaceManager::parseTokens(msg_modified)) + { + return; + } + ChatMngr.tell(player.toUtf8(), msg_modified); } else { diff --git a/code/ryzom/client/src/interface_v3/people_interraction.cpp b/code/ryzom/client/src/interface_v3/people_interraction.cpp index 17c6eadce..a8900a3ab 100644 --- a/code/ryzom/client/src/interface_v3/people_interraction.cpp +++ b/code/ryzom/client/src/interface_v3/people_interraction.cpp @@ -18,6 +18,7 @@ #include "stdpch.h" // client +#include "../string_manager_client.h" #include "people_interraction.h" #include "interface_expr.h" #include "interface_manager.h" @@ -756,6 +757,7 @@ void CPeopleInterraction::createGuildChat() chatDesc.Title = "uiGuildChat"; chatDesc.Listener = &GuildChatEntryHandler; chatDesc.Localize = true; + chatDesc.Savable = true; chatDesc.Id = "guild_chat"; chatDesc.AHOnCloseButton = "proc"; chatDesc.AHOnCloseButtonParams = "guild_chat_proc_close"; @@ -774,6 +776,7 @@ void CPeopleInterraction::createYuboChat() chatDesc.Title = "uiYuboChat"; chatDesc.Listener = &YuboChatEntryHandler; chatDesc.Localize = true; + chatDesc.Savable = true; chatDesc.Id = "yubo_chat"; chatDesc.AHOnCloseButton = "proc"; chatDesc.AHOnCloseButtonParams = "yubo_chat_proc_close"; @@ -781,6 +784,7 @@ void CPeopleInterraction::createYuboChat() YuboChat = getChatWndMgr().createChatWindow(chatDesc); if (!YuboChat) return; + YuboChat->setMenu(STD_CHAT_SOURCE_MENU); } @@ -795,6 +799,7 @@ void CPeopleInterraction::createDynamicChats() DynamicChatEntryHandler[i].DbIndex= i; chatDesc.Listener = &DynamicChatEntryHandler[i]; chatDesc.Localize = false; + chatDesc.Savable = true; chatDesc.ChatTemplate ="dynamic_chat_id"; chatDesc.ChatTemplateParams.push_back(make_pair(string("dyn_chat_nb"),toString(i))); chatDesc.Id = string("dynamic_chat") + toString(i); @@ -804,6 +809,8 @@ void CPeopleInterraction::createDynamicChats() chatDesc.HeaderColor = "UI:SAVE:WIN:COLORS:MEM"; DynamicChat[i] = getChatWndMgr().createChatWindow(chatDesc); + if (!DynamicChat[i]) continue; + DynamicChat[i]->setMenu(STD_CHAT_SOURCE_MENU); } } @@ -956,7 +963,23 @@ class CHandlerChatGroupFilter : public IActionHandler case CChatGroup::team: pUserBut->setHardText("uiFilterTeam"); break; case CChatGroup::guild: pUserBut->setHardText("uiFilterGuild"); break; case CChatGroup::universe: pUserBut->setHardText("uiFilterUniverse"); break; - // NB: user chat cannot have yubo_chat or dyn_chat target + case CChatGroup::dyn_chat: + uint32 index = PeopleInterraction.TheUserChat.Filter.getTargetDynamicChannelDbIndex(); + uint32 textId = pIM->getDbProp("SERVER:DYN_CHAT:CHANNEL"+toString(index)+":NAME")->getValue32(); + ucstring title; + STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title); + if (title.empty()) + { + // Dyn channel not available yet, so set to around + PeopleInterraction.TheUserChat.Filter.setTargetGroup(CChatGroup::arround); + pUserBut->setHardText("uiFilterAround"); + } + else + { + pUserBut->setHardText(title.toUtf8()); + } + break; + // NB: user chat cannot have yubo_chat target } pUserBut->setActive(true); @@ -1276,6 +1299,10 @@ void CPeopleInterraction::initContactLists( const std::vector &vFriendLi for (uint i = 0; i < vIgnoreListName.size(); ++i) addContactInList(contactIdPool++, vIgnoreListName[i], ccs_offline, 1); updateAllFreeTellerHeaders(); + + CInterfaceManager* pIM= CInterfaceManager::getInstance(); + CPeopleList::TSortOrder order = (CPeopleList::TSortOrder)(pIM->getDbProp("UI:SAVE:CONTACT_LIST:SORT_ORDER")->getValue32()); + FriendList.sortEx(order); } //================================================================================================================= @@ -1758,6 +1785,20 @@ void CPeopleInterraction::talkInDynamicChannel(uint32 channelNb,ucstring sentenc } } +//================================================================================================================= +void CPeopleInterraction::displayTellInMainChat(const ucstring &playerName) +{ + //CChatWindow *chat = PeopleInterraction.MainChat.Window; + CChatWindow *chat = PeopleInterraction.ChatGroup.Window; + if (!chat) return; + chat->getContainer()->setActive (true); + // make the container blink + chat->getContainer()->enableBlink(2); + // TODO : center the view on the newly created container ? + // display a new command '/name' in the chat. The player must enter a new unique name for the party chat. + chat->setCommand("tell " + playerName + " ", false); + chat->setKeyboardFocus(); +} ///////////////////////////////////// // ACTION HANDLERS FOR PEOPLE LIST // @@ -1945,22 +1986,6 @@ public: }; REGISTER_ACTION_HANDLER( CHandlerRemoveContact, "remove_contact"); - -//================================================================================================================= -static void displayTellInMainChat(const ucstring &playerName) -{ - //CChatWindow *chat = PeopleInterraction.MainChat.Window; - CChatWindow *chat = PeopleInterraction.ChatGroup.Window; - if (!chat) return; - chat->getContainer()->setActive (true); - // make the container blink - chat->getContainer()->enableBlink(2); - // TODO : center the view on the newly created container ? - // display a new command '/name' in the chat. The player must enter a new unique name for the party chat. - chat->setCommand("tell " + playerName + " ", false); - chat->setKeyboardFocus(); -} - //================================================================================================================= // Invoke the 'tell' command on a contact from its menu // The tell command is displayed in the 'around me' window @@ -1974,7 +1999,7 @@ public: uint peopleIndex; if (PeopleInterraction.getPeopleFromCurrentMenu(list, peopleIndex)) { - displayTellInMainChat(list->getName(peopleIndex)); + CPeopleInterraction::displayTellInMainChat(list->getName(peopleIndex)); } } }; @@ -1996,7 +2021,7 @@ class CHandlerTellContact : public IActionHandler uint peopleIndex; if (PeopleInterraction.getPeopleFromContainerID(gc->getId(), list, peopleIndex)) { - displayTellInMainChat(list->getName(peopleIndex)); + CPeopleInterraction::displayTellInMainChat(list->getName(peopleIndex)); } } @@ -2150,9 +2175,20 @@ class CHandlerSortContacts : public IActionHandler public: void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) { + CInterfaceManager* pIM= CInterfaceManager::getInstance(); + nlinfo("Load Order : %d", pIM->getDbProp("UI:SAVE:CONTACT_LIST:SORT_ORDER")->getValue32()); + CPeopleList::TSortOrder order = (CPeopleList::TSortOrder)(pIM->getDbProp("UI:SAVE:CONTACT_LIST:SORT_ORDER")->getValue32()); + + order = (CPeopleList::TSortOrder)(order + 1); + if (order == CPeopleList::END_SORT_ORDER) { + order = CPeopleList::START_SORT_ORDER; + } + + nlinfo("Save Order : %d", order); + pIM->getDbProp("UI:SAVE:CONTACT_LIST:SORT_ORDER")->setValue32((sint32)order); CPeopleList *pl = PeopleInterraction.getPeopleListFromCurrentMenu(); if (pl) - pl->sort(); + pl->sortEx(order); } }; REGISTER_ACTION_HANDLER( CHandlerSortContacts, "sort_contacts"); @@ -2441,6 +2477,30 @@ public: if (pMenuUniverse) pMenuUniverse->setGrayed (false); if (pMenuTeam) pMenuTeam->setGrayed (!teamActive); if (pMenuGuild) pMenuGuild->setGrayed (!guildActive); + + // Remove existing dynamic chats + while (pMenu->getNumLine() > 5) + pMenu->deleteLine(pMenu->getNumLine()-1); + + // Add dynamic chats + uint insertion_index = 0; + for (uint i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++) + { + string s = toString(i); + uint32 textId = im->getDbProp("SERVER:DYN_CHAT:CHANNEL"+s+":NAME")->getValue32(); + bool active = (textId != 0); + if (active) + { + uint32 canWrite = im->getDbProp("SERVER:DYN_CHAT:CHANNEL"+s+":WRITE_RIGHT")->getValue32(); + if (canWrite != 0) + { + ucstring title; + STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title); + pMenu->addLineAtIndex(5 + insertion_index, title+" @{T8}/"+s, "chat_target_selected", "dyn"+s, "dyn"+s); + insertion_index++; + } + } + } } // activate the menu @@ -2465,28 +2525,42 @@ class CHandlerChatTargetSelected : public IActionHandler CChatTargetFilter &cf = fc->Filter; // Team if (nlstricmp(sParams, "team") == 0) + { cf.setTargetGroup(CChatGroup::team); - + } // Guild - if (nlstricmp(sParams, "guild") == 0) + else if (nlstricmp(sParams, "guild") == 0) + { cf.setTargetGroup(CChatGroup::guild); - + } // Say - if (nlstricmp(sParams, "say") == 0) + else if (nlstricmp(sParams, "say") == 0) + { cf.setTargetGroup(CChatGroup::say); - + } // Shout - if (nlstricmp(sParams, "shout") == 0) + else if (nlstricmp(sParams, "shout") == 0) + { cf.setTargetGroup(CChatGroup::shout); - + } // Region - if (nlstricmp(sParams, "region") == 0) + else if (nlstricmp(sParams, "region") == 0) + { cf.setTargetGroup(CChatGroup::region); - + } // Universe - if (nlstricmp(sParams, "universe") == 0) + else if (nlstricmp(sParams, "universe") == 0) + { cf.setTargetGroup(CChatGroup::universe); - + } + else + { + for (uint i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++) { + if (nlstricmp(sParams, "dyn"+toString("%d", i)) == 0) { + cf.setTargetGroup(CChatGroup::dyn_chat, i); + } + } + } // Case of user chat in grouped chat window if (cw == PeopleInterraction.ChatGroup.Window) @@ -2593,6 +2667,25 @@ class CHandlerSelectChatSource : public IActionHandler // select main chat menu menu = dynamic_cast(im->getElementFromId(MAIN_CHAT_SOURCE_MENU)); + // Remove all unused dynamic channels and set the names + for (uint i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++) + { + string s = toString(i); + CViewTextMenu *pVTM = dynamic_cast(im->getElementFromId(MAIN_CHAT_SOURCE_MENU+":tab:dyn"+s)); + if (pVTM) + { + uint32 textId = im->getDbProp("SERVER:DYN_CHAT:CHANNEL"+s+":NAME")->getValue32(); + bool active = (textId != 0); + pVTM->setActive(active); + if (active) + { + ucstring title; + STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title); + pVTM->setText("["+s+"] " + title); + } + } + } + // Menu with Filters CChatGroupWindow *pWin = pi.getChatGroupWindow(); if (pWin->getTabIndex() == 5) // (5 == user) -> complete menu diff --git a/code/ryzom/client/src/interface_v3/people_interraction.h b/code/ryzom/client/src/interface_v3/people_interraction.h index 43391c718..183bb0f36 100644 --- a/code/ryzom/client/src/interface_v3/people_interraction.h +++ b/code/ryzom/client/src/interface_v3/people_interraction.h @@ -256,6 +256,8 @@ public: void updateAllFreeTellerHeaders(); void removeAllFreeTellers(); + + static void displayTellInMainChat(const ucstring &playerName); private: // create various chat & people lists void createTeamChat(); diff --git a/code/ryzom/client/src/interface_v3/people_list.cpp b/code/ryzom/client/src/interface_v3/people_list.cpp index d42c24a47..073c42daa 100644 --- a/code/ryzom/client/src/interface_v3/people_list.cpp +++ b/code/ryzom/client/src/interface_v3/people_list.cpp @@ -185,6 +185,85 @@ sint CPeopleList::getIndexFromContainerID(const std::string &id) const return -1; } +//================================================================== +bool CPeopleList::sortExByContactId(const CPeople& a, const CPeople& b) +{ + return (a.ContactId < b.ContactId); +} + +//================================================================== +bool CPeopleList::sortExByName(const CPeople& a, const CPeople& b) +{ + ucstring name_a = toUpper(a.getName()); + ucstring name_b = toUpper(b.getName()); + + return (name_a < name_b); +} + +//================================================================== +bool CPeopleList::sortExByOnline(const CPeople& a, const CPeople& b) +{ + ucstring name_a = toUpper(a.getName()); + ucstring name_b = toUpper(b.getName()); + + // We want order: online/alpha, offworld/alpha, offline/alpha + if (a.Online == b.Online) { + return (name_a < name_b); + } + else { + // Compare online status + switch (a.Online) { + case ccs_online: + // a is > if a is online + return true; + break; + case ccs_online_abroad: + // a is > if b is offline + return (b.Online == ccs_offline); + break; + case ccs_offline: + default: + // b is always > if a is offline + return false; + break; + } + } + + // Should not get here so just return something + return true; +} + +//================================================================== +void CPeopleList::sortEx(TSortOrder order) +{ + // remove all people from the father container + if (!_BaseContainer) return; + uint k; + for(k = 0; k < _Peoples.size(); ++k) + { + _BaseContainer->detachContainer(_Peoples[k].Container); + } + + switch (order) { + default: + case sort_index: + std::sort(_Peoples.begin(), _Peoples.end(), CPeopleList::sortExByContactId); + break; + case sort_name: + std::sort(_Peoples.begin(), _Peoples.end(), CPeopleList::sortExByName); + break; + + case sort_online: + std::sort(_Peoples.begin(), _Peoples.end(), CPeopleList::sortExByOnline); + break; + } + + for(k = 0; k < _Peoples.size(); ++k) + { + _BaseContainer->attachContainer(_Peoples[k].Container); + } +} + //================================================================== void CPeopleList::sort() { @@ -384,7 +463,14 @@ void CPeopleList::displayLocalPlayerTell(uint index,const ucstring &msg,uint num nlwarning(" can't get group list."); return; } - ucstring finalMsg = /*UserEntity->getName() + " " +*/ CI18N::get("youTell") + ": " + msg; + + ucstring cur_time; + if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false)->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + ucstring finalMsg = cur_time + csr + CI18N::get("youTell") + ": " + msg; // display msg with good color CInterfaceProperty prop; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); @@ -788,12 +874,19 @@ class CHandlerContactEntry : public IActionHandler if(text.size() == 0) return; + // Parse any tokens in the text + if ( ! CInterfaceManager::parseTokens(text)) + { + pEB->setInputString (string("")); + return; + } + // is it a command ? if(text[0] == '/') { CChatWindow::_ChatWindowLaunchingCommand = NULL; // no CChatWindow instance there .. // TODO : have NLMISC::ICommand accept unicode strings - std::string str = text.toString().substr(1); + std::string str = text.toUtf8().substr(1); NLMISC::ICommand::execute( str, g_log ); pEB->setInputString (string("")); return; @@ -804,6 +897,7 @@ class CHandlerContactEntry : public IActionHandler CGroupContainer *gc = pCaller->getParent()->getEnclosingContainer(); // title gives the name of the player ucstring playerName = gc->getUCTitle(); + // Simply do a tell on the player ChatMngr.tell(playerName.toString(), text); pEB->setInputString (string("")); @@ -816,8 +910,9 @@ class CHandlerContactEntry : public IActionHandler } // Retrieve name of the container in the list - string str = gc->getId().substr(0,gc->getId().rfind('_')); - if (str != "ui:interface:free_chat") + string ui_interface_free_chat = "ui:interface:free_chat"; + string str = gc->getId().substr(0, ui_interface_free_chat.length()); + if (str != ui_interface_free_chat) { string str2 = gc->getId().substr(gc->getId().rfind('_')+1,gc->getId().size()); str = str.substr(0,str.rfind('_')); @@ -839,7 +934,13 @@ class CHandlerContactEntry : public IActionHandler prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," "); ucstring final; CChatWindow::encodeColorTag(prop.getRGBA(), final, false); - final += CI18N::get("youTell")+": "; + ucstring cur_time; + if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false)->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + final += cur_time + csr + CI18N::get("youTell")+": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); CChatWindow::encodeColorTag(prop.getRGBA(), final, true); final += text; diff --git a/code/ryzom/client/src/interface_v3/people_list.h b/code/ryzom/client/src/interface_v3/people_list.h index caa7167cd..0ab0f3508 100644 --- a/code/ryzom/client/src/interface_v3/people_list.h +++ b/code/ryzom/client/src/interface_v3/people_list.h @@ -81,6 +81,18 @@ public: ucstring getName(uint index) const; // Sort people alphabetically void sort(); + + enum TSortOrder + { + sort_index = 0, + START_SORT_ORDER = sort_index, + sort_name, + sort_online, + END_SORT_ORDER + }; + + void sortEx(TSortOrder order); + /** Add a people to the list, and returns its index or -1 if the creation failed * If this is a team mate, tells its index so that ic can be bound to the database in the right location */ @@ -165,6 +177,10 @@ private: void updatePeopleMenu(uint index); // from CGroupContainer::IChildrenObs virtual void childrenMoved(uint srcIndex, uint destIndex, CGroupContainer *children); + + static bool sortExByContactId(const CPeople& a, const CPeople& b); + static bool sortExByName(const CPeople& a, const CPeople& b); + static bool sortExByOnline(const CPeople& a, const CPeople& b); }; #endif diff --git a/code/ryzom/client/src/interface_v3/view_bitmap.cpp b/code/ryzom/client/src/interface_v3/view_bitmap.cpp index f750ccaba..dba18c194 100644 --- a/code/ryzom/client/src/interface_v3/view_bitmap.cpp +++ b/code/ryzom/client/src/interface_v3/view_bitmap.cpp @@ -224,7 +224,7 @@ void CViewBitmap::setTexture(const std::string & TxName) // CInterfaceManager *pIM = CInterfaceManager::getInstance(); // CViewRenderer &rVR = pIM->getViewRenderer(); - _TextureId.setTexture (TxName.c_str (), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight); + _TextureId.setTexture (TxName.c_str (), _TxtOffsetX, _TxtOffsetY, _TxtWidth, _TxtHeight, false); } // ---------------------------------------------------------------------------- diff --git a/code/ryzom/client/src/light_cycle_manager.cpp b/code/ryzom/client/src/light_cycle_manager.cpp index ef12dc096..cb34c0dd0 100644 --- a/code/ryzom/client/src/light_cycle_manager.cpp +++ b/code/ryzom/client/src/light_cycle_manager.cpp @@ -344,16 +344,22 @@ void CLightCycleManager::setHour(float hour, const CWeatherManagerClient &wm, NL } _Touched = false; - // Set the Sun color - CRGBA color; - color.add(_LastDiffuse, lightningColor); - Scene->setLightGroupColor (LightGroupDay, color); - // Set the Night color - float nightLevel = _LightLevel*255.f; - clamp (nightLevel, 0, 255); - color.set ((uint8)nightLevel, (uint8)nightLevel, (uint8)nightLevel); - Scene->setLightGroupColor (LightGroupNight, color); + // Set the Sun color only if not indoor + if (ContinentMngr.cur()->Indoor) + { + Scene->setSunAmbient(CRGBA(150, 150, 150, 255)); + } + else + { + CRGBA color; + color.add(_LastDiffuse, lightningColor); + Scene->setLightGroupColor (LightGroupDay, color); + float nightLevel = _LightLevel*255.f; + clamp (nightLevel, 0, 255); + color.set ((uint8)nightLevel, (uint8)nightLevel, (uint8)nightLevel); + Scene->setLightGroupColor (LightGroupNight, color); + } if (Landscape) { diff --git a/code/ryzom/client/src/net_manager.cpp b/code/ryzom/client/src/net_manager.cpp index a13b9feab..dc9d3d756 100644 --- a/code/ryzom/client/src/net_manager.cpp +++ b/code/ryzom/client/src/net_manager.cpp @@ -606,7 +606,7 @@ static CInterfaceChatDisplayer InterfaceChatDisplayer; void CInterfaceChatDisplayer::colorizeSender(ucstring &text, const ucstring &senderName, CRGBA baseColor) { // find the sender/text separator to put color tags - ucstring::size_type pos = text.find(ucchar(':')); + ucstring::size_type pos = senderName.length() - 1; if (pos != ucstring::npos) { ucstring str; @@ -635,14 +635,17 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c bool bubbleWanted = true; + // Subtract rawMessage from ucstr so that the 'sender' part remains. + ucstring senderPart = ucstr.luabind_substr(0, ucstr.length() - rawMessage.length()); + // search a "{no_bubble}" tag { - ucstring::size_type index = finalString.find (ucstring("{no_bubble}")); - const size_t tokenSize= 11; // strlen de "{no_bubble}" + ucstring::size_type index = finalString.find(ucstring("{no_bubble}")); + const size_t tokenSize= 11; // length of "{no_bubble}" if (index != ucstring::npos) { bubbleWanted = false; - finalString = finalString.substr (0, index) + finalString.substr(index+tokenSize,finalString.size()); + finalString = finalString.luabind_substr(0, index) + finalString.substr(index+tokenSize,finalString.size()); } } @@ -654,9 +657,9 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c // Remove all {break} for(;;) { - ucstring::size_type index = finalString.find (ucstring("{break}")); + ucstring::size_type index = finalString.find(ucstring("{break}")); if (index == ucstring::npos) break; - finalString = finalString.substr (0, index) + finalString.substr(index+7,finalString.size()); + finalString = finalString.luabind_substr(0, index) + finalString.luabind_substr(index+7,finalString.size()); } // select DB @@ -696,7 +699,15 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c if (mode != CChatGroup::system) { // find the sender/text separator to put color tags - colorizeSender(finalString, senderName, col); + if (senderPart.empty() && stringCategory == "emt") + { + size_t pos = finalString.find(ucstring(": "), 0); + if (pos != ucstring::npos) + { + senderPart = finalString.luabind_substr(0, pos + 2); + } + } + colorizeSender(finalString, senderPart, col); } // play associated fx if any @@ -839,10 +850,15 @@ void CInterfaceChatDisplayer::displayTell(/*TDataSetIndex senderIndex, */const u prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); bool windowVisible; - - colorizeSender(finalString, senderName, prop.getRGBA()); - ucstring goodSenderName = CEntityCL::removeTitleAndShardFromName(senderName); + + // The sender part is up to and including the first ":" after the goodSenderName + ucstring::size_type pos = finalString.find(goodSenderName); + pos = finalString.find(':', pos); + pos = finalString.find(' ', pos); + ucstring senderPart = finalString.substr(0, pos+1); + colorizeSender(finalString, senderPart, prop.getRGBA()); + PeopleInterraction.ChatInput.Tell.displayTellMessage(/*senderIndex, */finalString, goodSenderName, prop.getRGBA(), 2, &windowVisible); // Open the free teller window @@ -1764,6 +1780,14 @@ void impulseTeamContactStatus(NLMISC::CBitMemStream &impulse) // 0<=FriendList (actually ignore list does not show online state) PeopleInterraction.updateContactInList(contactId, online, 0); + // Resort the contact list if needed + CInterfaceManager* pIM= CInterfaceManager::getInstance(); + CPeopleList::TSortOrder order = (CPeopleList::TSortOrder)(pIM->getDbProp("UI:SAVE:CONTACT_LIST:SORT_ORDER")->getValue32()); + + if (order == CPeopleList::sort_online) + { + PeopleInterraction.FriendList.sortEx(order); + } }// impulseTeamContactStatus // @@ -3069,7 +3093,7 @@ void impulseItemCloseRoomInventory(NLMISC::CBitMemStream &impulse) void impulseUserBars(NLMISC::CBitMemStream &impulse) { uint8 msgNumber; - sint16 hp, sap, sta, focus; + sint32 hp, sap, sta, focus; impulse.serial(msgNumber); impulse.serial(hp); impulse.serial(sap); @@ -3164,7 +3188,6 @@ private: if(contentStr.size()>=6 && contentStr[0]=='W' && contentStr[1]=='E' && contentStr[2]=='B' && contentStr[3]==' ' && contentStr[4]==':' && contentStr[5]==' ' ) { - ucstring web_app; uint i; const uint digitStart= 6; const uint digitMaxEnd= (uint)contentStr.size(); @@ -3176,18 +3199,17 @@ private: if(contentStr[i] == ' ') break; } - nlinfo("%d", i); if(i != digitMaxEnd) - web_app = contentStr.substr(digitStart, i-digitStart); + { + ucstring web_app = contentStr.substr(digitStart, i-digitStart); + contentStr = ucstring("http://atys.ryzom.com/start/")+web_app+ucstring(".php?")+contentStr.substr(i+1); + } else { - web_app = ucstring("index"); + contentStr = ""; i = digitStart; - nlinfo("no app"); } - contentStr = ucstring("http://atys.ryzom.com/start/")+web_app+ucstring(".php?")+contentStr.substr(i+1); - nlinfo("contentStr = %s", contentStr.toString().c_str()); - } + } else if(contentStr.size()>=5 && contentStr[0]=='@' && contentStr[1]=='{' && contentStr[2]=='W') { uint i; @@ -3218,12 +3240,19 @@ private: { CGroupContainer *pGC = dynamic_cast(pIM->getElementFromId("ui:interface:webig")); - pGC->setActive(true); - string url = contentStr.toString(); - addWebIGParams(url); - groupHtml->browse(url.c_str()); - pIM->setTopWindow(pGC); + if (contentStr.empty()) + { + pGC->setActive(false); + } + else + { + pGC->setActive(true); + string url = contentStr.toString(); + addWebIGParams(url); + groupHtml->browse(url.c_str()); + pIM->setTopWindow(pGC); + } } } else diff --git a/code/ryzom/client/src/player_cl.cpp b/code/ryzom/client/src/player_cl.cpp index c85708ab6..e757aaf6d 100644 --- a/code/ryzom/client/src/player_cl.cpp +++ b/code/ryzom/client/src/player_cl.cpp @@ -227,15 +227,21 @@ bool CPlayerCL::isEnemy () const } // Faction - if( getPvpMode()&PVP_MODE::PvpFactionFlagged && - (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged) ) + if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && + (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { - if( CFactionWarManager::getInstance()->areFactionsInWar(getPvpClan(),UserEntity->getPvpClan()) ) + // Check if is not ally + if (!isInTeam() && !isInGuild()) { - return true; + // Check for each Clan if is in opposition + for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) + { + if ((isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) || (isPvpAlly(i) && UserEntity->isPvpEnnemy(i))) + return true; + } } + } - return false; } // isEnemy // @@ -293,15 +299,24 @@ bool CPlayerCL::isAlly() const } // Faction - if( getPvpMode()&PVP_MODE::PvpFactionFlagged && + if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { - if( getPvpClan()!=PVP_CLAN::Neutral && UserEntity->getPvpClan()!=PVP_CLAN::Neutral ) + if (isInTeam() && isInGuild()) + return true; + + // Check for each Clan if is in opposition + for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) { - if( getPvpClan()==UserEntity->getPvpClan() ) - { + if ((isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) || (isPvpAlly(i) && UserEntity->isPvpEnnemy(i))) + return false; + } + + // Check for each Clan if is in same clan + for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) + { + if ((isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) || (isPvpAlly(i) && UserEntity->isPvpAlly(i))) return true; - } } } @@ -365,19 +380,19 @@ bool CPlayerCL::isNeutralPVP() const return true; } - // Faction - if( getPvpMode()&PVP_MODE::PvpFactionFlagged ) + if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && + (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { - // if only target is in faction pvp - if( !(UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged) ) + // Check for each Clan if is in opposition or same + for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) { - return true; - } - // else if factions not in war - if( CFactionWarManager::getInstance()->areFactionsInWar(getPvpClan(),UserEntity->getPvpClan())==false ) - { - return true; + if ((isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) || + (isPvpAlly(i) && UserEntity->isPvpEnnemy(i)) || + (isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) || + (isPvpAlly(i) && UserEntity->isPvpAlly(i))) + return false; } + return true; } return false; diff --git a/code/ryzom/client/src/user_entity.cpp b/code/ryzom/client/src/user_entity.cpp index cd9925e05..aebd2bcc9 100644 --- a/code/ryzom/client/src/user_entity.cpp +++ b/code/ryzom/client/src/user_entity.cpp @@ -198,6 +198,16 @@ CUserEntity::~CUserEntity() _MountSpeeds.release(); CInterfaceManager *pIM = CInterfaceManager::getInstance(); + + { + CCDBNodeLeaf *node = pIM->getDbProp("SERVER:USER:IS_INVISIBLE", false); + if (node) + { + ICDBNode::CTextId textId; + node->removeObserver(&_InvisibleObs, textId); + } + } + for(uint i=0;igetDbProp(toString("SERVER:USER:SKILL_POINTS_%d:VALUE", i), false); @@ -347,8 +357,17 @@ bool CUserEntity::build(const CEntitySheet *sheet) // virtual // Rebuild interface buildInSceneInterface (); - // Add an observer on skill points + // Add observer on invisible property CInterfaceManager *pIM = CInterfaceManager::getInstance(); + { + CCDBNodeLeaf *node = pIM->getDbProp("SERVER:USER:IS_INVISIBLE", false); + if (node) { + ICDBNode::CTextId textId; + node->addObserver(&_InvisibleObs, textId); + } + } + + // Add an observer on skill points for(uint i=0;ibuildInSceneInterface(); +} + //--------------------------------------------------- void CUserEntity::CSkillPointsObserver::update(ICDBNode* node ) { diff --git a/code/ryzom/client/src/user_entity.h b/code/ryzom/client/src/user_entity.h index 785a5d0f3..d3f814fc8 100644 --- a/code/ryzom/client/src/user_entity.h +++ b/code/ryzom/client/src/user_entity.h @@ -589,7 +589,12 @@ protected: }; CSkillPointsObserver _SkillPointObs[EGSPD::CSPType::EndSPType]; - + class CInvisibleObserver : public ICDBNode::IPropertyObserver + { + public : + virtual void update(ICDBNode* node); + }; + CInvisibleObserver _InvisibleObs; /// Fame observer class CFameObserver : public ICDBNode::IPropertyObserver