From 24dfa7bf3d1231d84b26042463e04cb761f06e7f Mon Sep 17 00:00:00 2001
From: kervala <devnull@localhost>
Date: Fri, 5 Nov 2010 09:45:24 +0100
Subject: [PATCH] Changed: #1135 Merge changes from Ryzom patch 1.10

---
 .../data/gamedev/interfaces_v3/config.xml     |  6 ++
 .../gamedev/interfaces_v3/game_config.xml     | 35 ++++++++-
 code/ryzom/client/src/client_chat_manager.cpp | 20 +++--
 code/ryzom/client/src/client_chat_manager.h   |  2 +-
 code/ryzom/client/src/commands.cpp            | 12 ++-
 code/ryzom/client/src/cursor_functions.cpp    | 10 ++-
 .../src/interface_v3/action_handler_help.cpp  | 14 +++-
 .../client/src/interface_v3/chat_filter.cpp   |  2 +-
 .../client/src/interface_v3/chat_window.cpp   | 71 ++++++++++++++++--
 .../interface_v3/filtered_chat_summary.cpp    | 10 ++-
 .../src/interface_v3/filtered_chat_summary.h  |  1 +
 .../client/src/interface_v3/group_html.cpp    | 75 +++++++++----------
 .../client/src/interface_v3/group_html.h      |  5 +-
 .../src/interface_v3/interface_manager.cpp    | 10 +--
 .../ryzom/client/src/interface_v3/lua_ihm.cpp | 18 +++++
 code/ryzom/client/src/interface_v3/lua_ihm.h  |  1 +
 .../src/interface_v3/people_interraction.cpp  | 47 ++++++++++--
 code/ryzom/client/src/net_manager.cpp         |  8 +-
 18 files changed, 267 insertions(+), 80 deletions(-)

diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/config.xml b/code/ryzom/client/data/gamedev/interfaces_v3/config.xml
index 88306afc5..e1b5be456 100644
--- a/code/ryzom/client/data/gamedev/interfaces_v3/config.xml
+++ b/code/ryzom/client/data/gamedev/interfaces_v3/config.xml
@@ -2960,12 +2960,18 @@ This MUST follow the Enum MISSION_DESC::TIconId
   <variable entry="UI:SAVE:CHAT:COLORS:TELL"
             type="rgba"
             value="170 170 170 255" />
+  <variable entry="UI:SAVE:CHAT:COLORS:DYN"
+            type="rgba"
+            value="162 255 173 255" />
   <variable entry="UI:SAVE:CHAT:ENTER_DONT_QUIT_CB"
             type="bool"
             value="false" />
   <variable entry="UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB"
              type="bool"
              value="false" />
+  <variable entry="UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB"
+             type="bool"
+             value="false" />
   <!-- ***************************** -->
   <!-- *    SYSTEM INFO COLORS     * -->
   <!-- ***************************** -->
diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/game_config.xml b/code/ryzom/client/data/gamedev/interfaces_v3/game_config.xml
index f15f3adc9..bd4b7ed40 100644
--- a/code/ryzom/client/data/gamedev/interfaces_v3/game_config.xml
+++ b/code/ryzom/client/data/gamedev/interfaces_v3/game_config.xml
@@ -1605,14 +1605,23 @@
                   posparent="cc_univ"
                   x="0"
                   y="-4" />
+        <instance template="tgcw_color"
+                  id="cc_dyn"
+                  text="uiDynColor"
+                  title="uiCCDDyn"
+                  tooltip="uittChooseDynColor"
+                  posref="BL TL"
+                  posparent="cc_shout"
+                  x="0"
+                  y="-4" />
         <instance template="tgcw_checkbox"
                   id="enter_dontquit_cb"
                   text="uiEnterDontQuitCB"
                   tooltip="uittChatEnter"
-                  posparent="cc_univ"
+                  posparent="cc_dyn"
                   posref="BL TL"
                   x="0"
-                  y="-25" />
+                  y="-8" />
         <instance template="tgcw_checkbox"
                   id="show_times_in_chat_cb"
                   text="uiShowTimesInChatCB"
@@ -1621,17 +1630,27 @@
                   posref="BL TL"
                   x="0"
                   y="-8" />
+        <instance template="tgcw_checkbox"
+                  id="show_dyn_channel_name_in_chat_cb"
+                  text="uiShowDynChannelNameInChatCB"
+                  tooltip="uittShowDynChannelNameInChat"
+                  posparent="show_times_in_chat_cb"
+                  posref="BL TL"
+                  x="0"
+                  y="-8" />
         <instance template="tgcw_scrollbarint"
                   id="font_size"
                   text="uiFontSize"
                   posref="BL TL"
-                  posparent="show_times_in_chat_cb"
+                  posparent="show_dyn_channel_name_in_chat_cb"
                   x="0"
                   y="-8" />
         <link expr="@UI:SAVE:CHAT:ENTER_DONT_QUIT_CB"
               target="enter_dontquit_cb:c:pushed" />
         <link expr="@UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB"
               target="show_times_in_chat_cb:c:pushed" />
+        <link expr="@UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB"
+              target="show_dyn_channel_name_in_chat_cb:c:pushed" />
       </group>
       <ctrl style="skin_scroll"
             id="sb_chat_colors"
@@ -3062,6 +3081,11 @@
                widget="colbut"
                link="UI:SAVE:CHAT:COLORS:SHOUT"
                realtime="true" />
+        <param ui="chat_colors:cc_dyn:c"
+               type="db"
+               widget="colbut"
+               link="UI:SAVE:CHAT:COLORS:DYN"
+               realtime="true" />
         <!--
                         <param ui="chat_colors:cc_shout:c"              type="db" widget="colbut" link="UI:SAVE:CHAT:COLORS:SHOUT" realtime="true" />
                         <param ui="chat_colors:cc_civi:c"               type="db" widget="colbut" link="UI:SAVE:CHAT:COLORS:CIVILIZATION" realtime="true" />
@@ -3077,6 +3101,11 @@
                widget="boolbut"
                link="UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB"
                realtime="true" />
+        <param ui="chat_colors:show_dyn_channel_name_in_chat_cb:c"
+               type="db"
+               widget="boolbut"
+               link="UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB"
+               realtime="true" />
         <param ui="chat_colors:font_size:c"
                type="db"
                widget="sbint"
diff --git a/code/ryzom/client/src/client_chat_manager.cpp b/code/ryzom/client/src/client_chat_manager.cpp
index 0d23e5cd4..86a28314d 100644
--- a/code/ryzom/client/src/client_chat_manager.cpp
+++ b/code/ryzom/client/src/client_chat_manager.cpp
@@ -1235,7 +1235,7 @@ REGISTER_ACTION_HANDLER( CHandlerEnterTell, "enter_tell");
 //	updateChatModeAndButton
 //
 //-----------------------------------------------
-void CClientChatManager::updateChatModeAndButton(uint mode)
+void CClientChatManager::updateChatModeAndButton(uint mode, uint32 dynamicChannelDbIndex)
 {
 	// Check if USER chat is active
 	bool userActive = false;
@@ -1274,11 +1274,19 @@ void CClientChatManager::updateChatModeAndButton(uint mode)
 					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();
+						uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(dynamicChannelDbIndex);
 						ucstring title;
 						STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title);
-						pUserBut->setHardText(title.toUtf8());
+						if (title.empty())
+						{
+							// Dyn channel does not exist, don't change
+							m = PeopleInterraction.TheUserChat.Filter.getTargetGroup();
+							dynamicChannelDbIndex = PeopleInterraction.TheUserChat.Filter.getTargetDynamicChannelDbIndex();
+						}
+						else
+						{
+							pUserBut->setHardText(title.toUtf8());
+						}
 						break;
 					// NB: user chat cannot have yubo_chat target
 				}
@@ -1300,8 +1308,8 @@ void CClientChatManager::updateChatModeAndButton(uint mode)
 				pEditBox->setX(pUserBut->getWReal()+4);
 			}
 
-			PeopleInterraction.TheUserChat.Filter.setTargetGroup(m);
-			PeopleInterraction.ChatGroup.Filter.setTargetGroup(m);
+			PeopleInterraction.TheUserChat.Filter.setTargetGroup(m, dynamicChannelDbIndex);
+			PeopleInterraction.ChatGroup.Filter.setTargetGroup(m, dynamicChannelDbIndex);
 		}
 	}
 }
diff --git a/code/ryzom/client/src/client_chat_manager.h b/code/ryzom/client/src/client_chat_manager.h
index 7ac43577a..8a591a27d 100644
--- a/code/ryzom/client/src/client_chat_manager.h
+++ b/code/ryzom/client/src/client_chat_manager.h
@@ -208,7 +208,7 @@ public :
 	/**
 	 * update chat mode button
      */
-	void updateChatModeAndButton(uint mode);
+	void updateChatModeAndButton(uint mode, uint32 dynamicChannelDbIndex = 0);
 
 	/**
 	 * Get the string for a tell. display now if ready or delay in flushBuffer()
diff --git a/code/ryzom/client/src/commands.cpp b/code/ryzom/client/src/commands.cpp
index d908d0fc4..eab515137 100644
--- a/code/ryzom/client/src/commands.cpp
+++ b/code/ryzom/client/src/commands.cpp
@@ -1126,7 +1126,11 @@ NLMISC_COMMAND(execScript, "Execute a script file (.cmd)","<FileName>")
 					if(strncmp(line, "/*", 2)==0)
 						inComment++;
 					if(inComment<=0)
-						ICommand::execute(line, g_log);
+					{
+						ucstring ucline(line);
+						CInterfaceManager::parseTokens(ucline);
+						ICommand::execute(ucline.toUtf8(), g_log);
+					}
 					if(strncmp(line, "*/", 2)==0)
 						inComment--;
 				}
@@ -1216,12 +1220,14 @@ static bool talkInChan(uint32 nb,std::vector<std::string>args)
 			tmp = tmp+" ";
 		}
 
-		PeopleInterraction.talkInDynamicChannel(nb,ucstring(tmp));
+		ucstring uctmp;
+		uctmp.fromUtf8(tmp);
+		PeopleInterraction.talkInDynamicChannel(nb, uctmp);
 		return true;
 	}
 	else
 	{
-		ChatMngr.updateChatModeAndButton(CChatGroup::dyn_chat);
+		ChatMngr.updateChatModeAndButton(CChatGroup::dyn_chat, nb);
 	}
 	return false;
 }
diff --git a/code/ryzom/client/src/cursor_functions.cpp b/code/ryzom/client/src/cursor_functions.cpp
index 634019d25..437624a86 100644
--- a/code/ryzom/client/src/cursor_functions.cpp
+++ b/code/ryzom/client/src/cursor_functions.cpp
@@ -522,6 +522,11 @@ void checkUnderCursor()
 		else
 		{
 			CShapeInstanceReference instref = EntitiesMngr.getShapeInstanceUnderPos(cursX, cursY);
+			
+			bool cleanSelectedInstance = EntitiesMngr.instancesRemoved();
+			if (cleanSelectedInstance)
+				selectedInstance = noSelectedInstance;
+
 			UInstance instance = instref.Instance;
 			if (!instance.empty())
 			{
@@ -865,9 +870,12 @@ void contextWebIG(bool rightClick, bool dblClick)
 {
 	CInterfaceManager *IM = CInterfaceManager::getInstance();
 	CInterfaceElement *pGC = IM->getElementFromId("ui:interface:bot_chat_object");
-	CInterface3DShape *el= dynamic_cast<CInterface3DShape*>(IM->getElementFromId("ui:interface:bot_chat_object:scene3d:object"));
+	CInterface3DShape *el= dynamic_cast<CInterface3DShape*>(IM->getElementFromId("ui:interface:bot_chat_object:scene3d:object_1"));
 	if (el != NULL)
+	{
 		el->setName(selectedInstance.getShapeName());
+		el->setPosX(0.0f);
+	}
 	if (selectedInstanceURL.empty())
 	{
 		if (pGC != NULL)
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 1ac2e025b..6e6628b30 100644
--- a/code/ryzom/client/src/interface_v3/action_handler_help.cpp
+++ b/code/ryzom/client/src/interface_v3/action_handler_help.cpp
@@ -948,6 +948,9 @@ class CHandlerBrowse : public IActionHandler
 					}
 				}
 
+				ucstring ucparams(params);
+				CInterfaceManager::parseTokens(ucparams);
+				params = ucparams.toUtf8();
 				// go. NB: the action handler himself may translate params from utf8
 				CInterfaceManager::getInstance()->runActionHandler(action, elementGroup, params);
 
@@ -2406,10 +2409,13 @@ void setupItemPreview(CSheetHelpSetup &setup, CItemSheet *pIS)
 	else if (pIS->Family == ITEMFAMILY::SHIELD)
 	{
 		cs.VisualPropA.PropertySubData.WeaponLeftHand = CVisualSlotManager::getInstance()->sheet2Index( CSheetId(setup.SrcSheet->getSheetId()), SLOTTYPE::LEFT_HAND_SLOT );
-		CItemSheet *pES = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, cs.VisualPropA.PropertySubData.WeaponRightHand);
-		if (pES->ItemType == ITEM_TYPE::TWO_HAND_AXE || pES->ItemType == ITEM_TYPE::TWO_HAND_MACE || pES->ItemType == ITEM_TYPE::TWO_HAND_SWORD ||
-			pES->ItemType == ITEM_TYPE::MAGICIAN_STAFF || pES->ItemType == ITEM_TYPE::AUTOLAUCH || pES->ItemType == ITEM_TYPE::LAUNCHER || pES->ItemType == ITEM_TYPE::RIFLE)
-			cs.VisualPropA.PropertySubData.WeaponRightHand = 0;
+		if (cs.VisualPropA.PropertySubData.WeaponRightHand != 0)
+		{
+			CItemSheet *pES = SheetMngr.getItem(SLOTTYPE::RIGHT_HAND_SLOT, cs.VisualPropA.PropertySubData.WeaponRightHand);
+			if (pES->ItemType == ITEM_TYPE::TWO_HAND_AXE || pES->ItemType == ITEM_TYPE::TWO_HAND_MACE || pES->ItemType == ITEM_TYPE::TWO_HAND_SWORD ||
+				pES->ItemType == ITEM_TYPE::MAGICIAN_STAFF || pES->ItemType == ITEM_TYPE::AUTOLAUCH || pES->ItemType == ITEM_TYPE::LAUNCHER || pES->ItemType == ITEM_TYPE::RIFLE)
+				cs.VisualPropA.PropertySubData.WeaponRightHand = 0;
+		}
 		SCharacter3DSetup::setupDBFromCharacterSummary("UI:TEMP:CHAR3D", cs);
 
 	}
diff --git a/code/ryzom/client/src/interface_v3/chat_filter.cpp b/code/ryzom/client/src/interface_v3/chat_filter.cpp
index da0645ed6..8c937d982 100644
--- a/code/ryzom/client/src/interface_v3/chat_filter.cpp
+++ b/code/ryzom/client/src/interface_v3/chat_filter.cpp
@@ -379,7 +379,7 @@ void CChatTargetFilter::setTargetGroup(CChatGroup::TGroupType groupType, uint32
 		const bool guildActive = pIM->getDbProp("SERVER:GUILD:NAME")->getValueBool();
 		switch(groupType)
 		{
-			case CChatGroup::dyn_chat:	// dyn_chat takes the color of say
+			case CChatGroup::dyn_chat: entry+="DYN"; break;
 			case CChatGroup::say:	entry+="SAY";	break;
 			case CChatGroup::shout:	entry+="SHOUT";	break;
 			case CChatGroup::team:	if(!teamActive) return; entry+="GROUP";	break;
diff --git a/code/ryzom/client/src/interface_v3/chat_window.cpp b/code/ryzom/client/src/interface_v3/chat_window.cpp
index e8378ea10..b76ab21cd 100644
--- a/code/ryzom/client/src/interface_v3/chat_window.cpp
+++ b/code/ryzom/client/src/interface_v3/chat_window.cpp
@@ -199,7 +199,7 @@ bool CChatWindow::isVisible() const
 }
 
 //=================================================================================
-void CChatWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CChatGroup::TGroupType /* gt */, uint32 /* dynamicChatDbIndex */, uint numBlinks /* = 0*/, bool *windowVisible /*= NULL*/)
+void CChatWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, uint numBlinks /* = 0*/, bool *windowVisible /*= NULL*/)
 {
 	if (!_Chat)
 	{
@@ -210,8 +210,26 @@ void CChatWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CChatGr
 
 	CChatTextManager &ctm = getChatTextMngr();
 
+	ucstring newmsg = msg;
+	ucstring prefix;
+	if (gt == CChatGroup::dyn_chat)
+	{
+		prefix = "[" + NLMISC::toString(dynamicChatDbIndex) + "]";
+		// Find position to put the new string
+		// After timestamp?
+		size_t pos = msg.find(ucstring("]"));
+		if (pos == ucstring::npos)
+		{
+			// No timestamp, so put it right after the color and add a space
+			pos = msg.find(ucstring("}"));
+			prefix += " ";
+		}
+		newmsg = msg.substr(0, pos + 1) + prefix + msg.substr(pos + 1);
+		prefix.clear();
+	}
+
 	gl = dynamic_cast<CGroupList *>(_Chat->getGroup("cb:text_list"));
-	if (gl)	gl->addChild(ctm.createMsgText(msg, col));
+	if (gl)	gl->addChild(ctm.createMsgText(newmsg, col));
 
 	// if the group is closed, make it blink
 	if (!_Chat->isOpen())
@@ -557,15 +575,32 @@ void CChatGroupWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CC
 	CInterfaceManager	*pIM= CInterfaceManager::getInstance();
 	CRGBA	newMsgColor= stringToRGBA(pIM->getDefine("chat_group_tab_color_newmsg").c_str());
 
+	ucstring newmsg = msg;
+	ucstring prefix;
+	if (gt == CChatGroup::dyn_chat)
+	{
+		prefix = "[" + NLMISC::toString(dynamicChatDbIndex) + "]";
+		// Find position to put the new string
+		// After timestamp?
+		size_t pos = msg.find(ucstring("]"));
+		if (pos == ucstring::npos)
+		{
+			// No timestamp, so put it right after the color and add a space
+			pos = msg.find(ucstring("}"));
+			prefix += " ";
+		}
+		newmsg = msg.substr(0, pos + 1) + prefix + msg.substr(pos + 1);
+		prefix.clear();
+	}
+
 	if (gl != NULL)
 	{
-		gl->addChild(ctm.createMsgText(msg, col));
+		gl->addChild(ctm.createMsgText(newmsg, col));
 		if (!gl->getParent()->getActive())
 			if (tab != NULL)
 				tab->setTextColorNormal(newMsgColor);
 	}
 
-
 	// *** Display the message in the UserChat (special case)
 	{
 		tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab5"));
@@ -585,14 +620,36 @@ void CChatGroupWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CC
 			case CChatGroup::guild:		if (ci.Guild.isListeningWindow(cw))			gl = gl2;	break;
 			case CChatGroup::system:	if (ci.SystemInfo.isListeningWindow(cw))	gl = gl2;	break;
 			case CChatGroup::universe:	if (ci.Universe.isListeningWindow(cw))		gl = gl2;	break;
-				// NB: the yubo chat and dyn_chat cannot be in a user chat
+			case CChatGroup::dyn_chat:	
+				if (ci.DynamicChat[dynamicChatDbIndex].isListeningWindow(cw))
+				{
+					gl = gl2;
+
+					// Add dynchannel number and optionally name before text if user channel
+					if (CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB", false)->getValueBool())
+					{
+						uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(dynamicChatDbIndex);
+						ucstring title;
+						STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title);
+						if ( ! title.empty())
+						{
+							prefix = " " + title;
+						}
+					}
+					
+					// Put the new prefix in the correct position
+					size_t pos = newmsg.find(ucstring("] "));
+					newmsg = newmsg.substr(0, pos) + prefix + newmsg.substr(pos);
+				}
+				break;
+
+				// NB: the yubo chat cannot be in a user chat
 			case CChatGroup::yubo_chat:	gl = NULL;	break;
-			case CChatGroup::dyn_chat:	gl = NULL;	break;
 		}
 
 		if (gl != NULL)
 		{
-			gl->addChild(ctm.createMsgText(msg, col));
+			gl->addChild(ctm.createMsgText(newmsg, col));
 			if (!gl->getParent()->getActive())
 				if (tab != NULL)
 					tab->setTextColorNormal(newMsgColor);
diff --git a/code/ryzom/client/src/interface_v3/filtered_chat_summary.cpp b/code/ryzom/client/src/interface_v3/filtered_chat_summary.cpp
index a48ccfe7d..c0d9dd30f 100644
--- a/code/ryzom/client/src/interface_v3/filtered_chat_summary.cpp
+++ b/code/ryzom/client/src/interface_v3/filtered_chat_summary.cpp
@@ -22,7 +22,7 @@
 //===================================================================================
 void CFilteredChatSummary::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
 {
-	sint ver= f.serialVersion(2);
+	sint ver = f.serialVersion(3);
 	f.serialCheck((uint32) 'USHC');
 	f.serial(SrcGuild);
 	f.serial(SrcTeam);
@@ -36,4 +36,12 @@ void CFilteredChatSummary::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
 
 	if(ver>=2)
 		f.serial(SrcRegion);
+
+	if (ver >= 3)
+	{
+		for (uint8 i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++) {
+			f.serial(SrcDynChat[i]);
+		}
+	}
+
 }
diff --git a/code/ryzom/client/src/interface_v3/filtered_chat_summary.h b/code/ryzom/client/src/interface_v3/filtered_chat_summary.h
index 5cdf4f3ac..193a834d0 100644
--- a/code/ryzom/client/src/interface_v3/filtered_chat_summary.h
+++ b/code/ryzom/client/src/interface_v3/filtered_chat_summary.h
@@ -33,6 +33,7 @@ public:
 	bool SrcSystemInfo;
 	bool SrcRegion;
 	bool SrcUniverse;
+	bool SrcDynChat[CChatGroup::MaxDynChanPerPlayer];
 	// output
 	CChatGroup::TGroupType Target;
 public:
diff --git a/code/ryzom/client/src/interface_v3/group_html.cpp b/code/ryzom/client/src/interface_v3/group_html.cpp
index de5ad2e0b..00caed7e7 100644
--- a/code/ryzom/client/src/interface_v3/group_html.cpp
+++ b/code/ryzom/client/src/interface_v3/group_html.cpp
@@ -111,7 +111,7 @@ void CGroupHTML::addImageDownload(const string &url, CViewBase *img)
 	curl_easy_setopt(curl, CURLOPT_FILE, fp);
 
 	curl_multi_add_handle(MultiCurl, curl);
-	Curls.push_back(CDataDownload(curl, url, fp, ImgType, img, ""));
+	Curls.push_back(CDataDownload(curl, url, fp, ImgType, img, "", ""));
 #ifdef LOG_DL
 	nlwarning("adding handle %x, %d curls", curl, Curls.size());
 #endif
@@ -146,7 +146,7 @@ string CGroupHTML::localBnpName(const string &url)
 }
 
 // Add a bnp download request in the multi_curl, return true if already downloaded
-bool CGroupHTML::addBnpDownload(const string &url, const string &action, const string &script)
+bool CGroupHTML::addBnpDownload(const string &url, const string &action, const string &script, const string &md5sum)
 {
 	// Search if we are not already downloading this url.
 	for(uint i = 0; i < Curls.size(); i++)
@@ -160,18 +160,17 @@ bool CGroupHTML::addBnpDownload(const string &url, const string &action, const s
 		}
 	}
 
-	CURL *curl = curl_easy_init();
-	if (!MultiCurl || !curl)
-		return false;
-
-	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true);
-	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
-
 	string dest = localBnpName(url);
+	string tmpdest = localBnpName(url)+".tmp";
 #ifdef LOG_DL
 	nlwarning("add to download '%s' dest '%s'", url.c_str(), dest.c_str());
 #endif
-	// create the local file
+	
+	// erase the tmp file if exists
+	if (NLMISC::CFile::fileExists(tmpdest))
+		NLMISC::CFile::deleteFile(tmpdest);
+
+	// create/delete the local file
 	if (NLMISC::CFile::fileExists(dest))
 	{
 		if (action == "override" || action == "delete")
@@ -186,21 +185,31 @@ bool CGroupHTML::addBnpDownload(const string &url, const string &action, const s
 	}
 	if (action != "delete")
 	{
-		FILE *fp = fopen (dest.c_str(), "wb");
+		CURL *curl = curl_easy_init();
+		if (!MultiCurl || !curl)
+			return false;
+
+		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true);
+		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+
+		FILE *fp = fopen (tmpdest.c_str(), "wb");
 		if (fp == NULL)
 		{
-			nlwarning("Can't open file '%s' for writing: code=%d '%s'", dest.c_str (), errno, strerror(errno));
+			nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest.c_str (), errno, strerror(errno));
 			return false;
 		}
 		curl_easy_setopt(curl, CURLOPT_FILE, fp);
 
 		curl_multi_add_handle(MultiCurl, curl);
-		Curls.push_back(CDataDownload(curl, url, fp, BnpType, NULL, script));
+		Curls.push_back(CDataDownload(curl, url, fp, BnpType, NULL, script, md5sum));
 #ifdef LOG_DL
 		nlwarning("adding handle %x, %d curls", curl, Curls.size());
 #endif
 		RunningCurls++;
 	}
+	else
+		return true;
+
 	return false;
 }
 
@@ -259,30 +268,29 @@ void CGroupHTML::checkDownloads()
 						string file;
 
 						if (it->type == ImgType)
-							file = localImageName(it->url)+".tmp";
+							file = localImageName(it->url);
 						else
 							file = localBnpName(it->url);
 
-						if(res != CURLE_OK || r < 200 || r >= 300)
+						if(res != CURLE_OK || r < 200 || r >= 300 || ((it->md5sum != "") && (it->md5sum != getMD5(file+".tmp").toString())))
 						{
-							NLMISC::CFile::deleteFile(file.c_str());
+							NLMISC::CFile::deleteFile((file+".tmp").c_str());
 						}
 						else
 						{
 							string finalUrl;
 							if (it->type == ImgType)
 							{
-								string image = localImageName(it->url);
-								CFile::moveFile(image.c_str(), (image+".tmp").c_str());
-								if (lookupLocalFile (finalUrl, image.c_str(), false))
+								CFile::moveFile(file.c_str(), (file+".tmp").c_str());
+								if (lookupLocalFile (finalUrl, file.c_str(), false))
 								{
 									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);
+										CBitmap::loadSize (file, w, h);
 										if (w == 0 || h == 0 || ((!NLMISC::isPowerOf2(w) || !NLMISC::isPowerOf2(h)) && !NL3D::CTextureFile::supportNonPowerOfTwoTextures()))
-											image.clear();
+											file.clear();
 
 										CCtrlButton *btn = dynamic_cast<CCtrlButton*>(it->imgs[i]);
 										if(btn)
@@ -290,8 +298,8 @@ void CGroupHTML::checkDownloads()
 	#ifdef LOG_DL
 											nlwarning("refresh new downloading image %d button %p", i, it->imgs[i]);
 	#endif
-											btn->setTexture (image);
-											btn->setTexturePushed(image);
+											btn->setTexture (file);
+											btn->setTexturePushed(file);
 											btn->invalidateCoords();
 											btn->invalidateContent();
 											btn->resetInvalidCoords();
@@ -306,7 +314,7 @@ void CGroupHTML::checkDownloads()
 	#ifdef LOG_DL
 												nlwarning("refresh new downloading image %d image %p", i, it->imgs[i]);
 	#endif
-												btm->setTexture (image);
+												btm->setTexture (file);
 												btm->invalidateCoords();
 												btm->invalidateContent();
 												btm->resetInvalidCoords();
@@ -319,25 +327,15 @@ void CGroupHTML::checkDownloads()
 							}
 							else
 							{
+								CFile::moveFile(file.c_str(), (file+".tmp").c_str());
 								if (lookupLocalFile (finalUrl, file.c_str(), false))
 								{
-									bool memoryCompressed = CPath::isMemoryCompressed();
-									if (memoryCompressed)
-									{
-										CPath::memoryUncompress();
-									}
-									CPath::addSearchPath("user/", true, false, NULL);
-									if (memoryCompressed)
-									{
-										CPath::memoryCompress();
-									}
+									
 									CInterfaceManager *pIM = CInterfaceManager::getInstance();
-									pIM->executeLuaScript(_ObjectScript, true);
-									_ObjectScript = "";
+									pIM->executeLuaScript(it->luaScript, true);
 								}
 							}
 						}
-
 						Curls.erase(it);
 						break;
 					}
@@ -1499,11 +1497,12 @@ void CGroupHTML::endElement (uint element_number)
 			{
 				if (!_ObjectData.empty())
 				{
-					if (addBnpDownload(_ObjectData, _ObjectAction, _ObjectScript))
+					if (addBnpDownload(_ObjectData, _ObjectAction, _ObjectScript, _ObjectMD5Sum))
 					{
 						CInterfaceManager *pIM = CInterfaceManager::getInstance();
 						pIM->executeLuaScript(_ObjectScript, true);
 					}
+					_ObjectScript = "";
 				}
 			}
 			_Object = false;
diff --git a/code/ryzom/client/src/interface_v3/group_html.h b/code/ryzom/client/src/interface_v3/group_html.h
index 98740d871..b4f4ee00b 100644
--- a/code/ryzom/client/src/interface_v3/group_html.h
+++ b/code/ryzom/client/src/interface_v3/group_html.h
@@ -540,7 +540,7 @@ private:
 
 	struct CDataDownload
 	{
-		CDataDownload(CURL *c, const std::string &u, FILE *f, TDataType t, CViewBase *i, const std::string &s) : curl(c), url(u), luaScript(s), type(t), fp(f)
+		CDataDownload(CURL *c, const std::string &u, FILE *f, TDataType t, CViewBase *i, const std::string &s, const std::string &m) : curl(c), url(u), luaScript(s), md5sum(m), type(t), fp(f)
 		{
 			if (t == ImgType) imgs.push_back(i);
 		}
@@ -548,6 +548,7 @@ private:
 		CURL *curl;
 		std::string url;
 		std::string luaScript;
+		std::string md5sum;
 		TDataType type;
 		FILE *fp;
 		std::vector<CViewBase *> imgs;
@@ -567,7 +568,7 @@ private:
 	// BnpDownload system
 	void initBnpDownload();
 	void checkBnpDownload();
-	bool addBnpDownload(const std::string &url, const std::string &action, const std::string &script);
+	bool addBnpDownload(const std::string &url, const std::string &action, const std::string &script, const std::string &md5sum);
 	std::string localBnpName(const std::string &url);
 
 	void releaseDownloads();
diff --git a/code/ryzom/client/src/interface_v3/interface_manager.cpp b/code/ryzom/client/src/interface_v3/interface_manager.cpp
index 60317aa1a..47504cba5 100644
--- a/code/ryzom/client/src/interface_v3/interface_manager.cpp
+++ b/code/ryzom/client/src/interface_v3/interface_manager.cpp
@@ -6348,8 +6348,8 @@ bool CInterfaceManager::parseTokens(ucstring& ucstr)
 		// Get the whole token substring first
 		end_pos = str.find(end_token, start_pos + 1);
 
-		if ((start_pos == string::npos) || 
-			(end_pos   == string::npos) || 
+		if ((start_pos == ucstring::npos) || 
+			(end_pos   == ucstring::npos) || 
 			(end_pos   <= start_pos + 1))
 		{
 			// Wrong formatting; give up on this one.
@@ -6566,17 +6566,15 @@ bool CInterfaceManager::parseTokens(ucstring& ucstr)
 		}
 
 
-		// Replace all occurances of token with the replacement
+		// Replace token
 		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)
+		if (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);
 		}
 	}
 
diff --git a/code/ryzom/client/src/interface_v3/lua_ihm.cpp b/code/ryzom/client/src/interface_v3/lua_ihm.cpp
index 93ecf2581..488d8d2fc 100644
--- a/code/ryzom/client/src/interface_v3/lua_ihm.cpp
+++ b/code/ryzom/client/src/interface_v3/lua_ihm.cpp
@@ -1351,6 +1351,7 @@ void	CLuaIHM::registerIHM(CLuaState &ls)
 	ls.registerFunc("enableModalWindow", enableModalWindow);
 	ls.registerFunc("disableModalWindow", disableModalWindow);
 	ls.registerFunc("getPlayerPos", getPlayerPos);
+	ls.registerFunc("addSearchPathUser", addSearchPathUser);
 	ls.registerFunc("displaySystemInfo", displaySystemInfo);
 	ls.registerFunc("disableContextHelpForControl", disableContextHelpForControl);
 	ls.registerFunc("disableContextHelp", disableContextHelp);
@@ -4244,6 +4245,23 @@ int CLuaIHM::getPlayerPos(CLuaState &ls)
 	return 3;
 }
 
+// ***************************************************************************
+int CLuaIHM::addSearchPathUser(CLuaState &ls)
+{
+	//H_AUTO(Lua_CLuaIHM_addSearchPathUser)
+	bool memoryCompressed = CPath::isMemoryCompressed();
+	if (memoryCompressed)
+	{
+		CPath::memoryUncompress();
+	}
+	CPath::addSearchPath("user/", true, false, NULL);
+	if (memoryCompressed)
+	{
+		CPath::memoryCompress();
+	}
+	return 0;
+}
+
 // ***************************************************************************
 int CLuaIHM::isPlayerFreeTrial(CLuaState &ls)
 {
diff --git a/code/ryzom/client/src/interface_v3/lua_ihm.h b/code/ryzom/client/src/interface_v3/lua_ihm.h
index 8f3f42a91..d773ae9ec 100644
--- a/code/ryzom/client/src/interface_v3/lua_ihm.h
+++ b/code/ryzom/client/src/interface_v3/lua_ihm.h
@@ -346,6 +346,7 @@ private:
 	static int	enableModalWindow(CLuaState &ls);
 	static int	disableModalWindow(CLuaState &ls);
 	static int	getPlayerPos(CLuaState &ls);
+	static int  addSearchPathUser(CLuaState &ls);
 	static int  getClientCfgVar(CLuaState &ls);
 	static int	isPlayerFreeTrial(CLuaState &ls);
 	static int	isPlayerNewbie(CLuaState &ls);
diff --git a/code/ryzom/client/src/interface_v3/people_interraction.cpp b/code/ryzom/client/src/interface_v3/people_interraction.cpp
index 75706156d..ee798b8a2 100644
--- a/code/ryzom/client/src/interface_v3/people_interraction.cpp
+++ b/code/ryzom/client/src/interface_v3/people_interraction.cpp
@@ -487,9 +487,6 @@ void CPeopleInterraction::initStdInputs()
 	if (YuboChat)
 		ChatInput.YuboChat.addListeningWindow(YuboChat);
 
-	// NB: The universe channel can only be seen from the user chat (and hence chat group)
-	// There is no Special universe window
-
 	if (TheUserChat.Window)
 	{
 		ChatInput.AroundMe.addListeningWindow(TheUserChat.Window);
@@ -498,6 +495,11 @@ void CPeopleInterraction::initStdInputs()
 		ChatInput.Guild.addListeningWindow(TheUserChat.Window);
 		ChatInput.Universe.addListeningWindow	(TheUserChat.Window);
 		// Don't add the system info by default
+		// Dynamic chats
+		for(i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++)
+		{
+			ChatInput.DynamicChat[i].addListeningWindow(TheUserChat.Window);
+		}
 	}
 
 	ChatInput.Tell.addListeningPeopleList(&FriendList);
@@ -1572,6 +1574,7 @@ void CPeopleInterraction::buildFilteredChatSummary(const CFilteredChat &src, CFi
 	fcs.SrcTell		  = ChatInput.Tell.isListeningWindow(src.Window);
 	fcs.SrcRegion     = ChatInput.Region.isListeningWindow(src.Window);
 	fcs.SrcUniverse   = ChatInput.Universe.isListeningWindow(src.Window);
+
 	// fill target infos
 	if (src.Filter.getTargetPartyChat() != NULL || !src.Filter.getTargetPlayer().empty())
 	{
@@ -1581,6 +1584,11 @@ void CPeopleInterraction::buildFilteredChatSummary(const CFilteredChat &src, CFi
 	{
 		fcs.Target = src.Filter.getTargetGroup();
 	}
+
+	for (uint8 i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++)
+	{
+		fcs.SrcDynChat[i] = ChatInput.DynamicChat[i].isListeningWindow(src.Window);
+	}
 }
 
 //=================================================================================================================
@@ -1725,6 +1733,11 @@ void CPeopleInterraction::setupUserChatFromSummary(const CFilteredChatSummary &s
 	ChatInput.Tell.setWindowState(dest.Window, summary.SrcTell);
 	ChatInput.Region.setWindowState(dest.Window, summary.SrcRegion);
 	ChatInput.Universe.setWindowState(dest.Window, summary.SrcUniverse);
+
+	for (uint8 i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++)
+	{
+		ChatInput.DynamicChat[i].setWindowState(dest.Window, summary.SrcDynChat[i]);
+	}
 }
 
 //=================================================================================================================
@@ -2491,7 +2504,7 @@ public:
 			for (uint i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++)
 			{
 				string s = toString(i);
-				uint32 textId = im->getDbProp("SERVER:DYN_CHAT:CHANNEL"+s+":NAME")->getValue32();
+				uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(i);
 				bool active = (textId != 0);
 				if (active)
 				{
@@ -2678,7 +2691,7 @@ class CHandlerSelectChatSource : public IActionHandler
 				CViewTextMenu *pVTM = dynamic_cast<CViewTextMenu *>(im->getElementFromId(MAIN_CHAT_SOURCE_MENU+":tab:dyn"+s));
 				if (pVTM)
 				{
-					uint32 textId = im->getDbProp("SERVER:DYN_CHAT:CHANNEL"+s+":NAME")->getValue32();
+					uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(i);
 					bool active = (textId != 0);
 					pVTM->setActive(active);
 					if (active)
@@ -2799,6 +2812,22 @@ class CHandlerSelectChatSource : public IActionHandler
 					++ insertionIndex;
 				}
 			}
+
+			// Add all existing dynamic channels and set the names
+			for (uint8 i = 0; i < CChatGroup::MaxDynChanPerPlayer; i++)
+			{
+				string s = toString(i);
+				uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(i);
+				bool active = (textId != 0);
+				if (active)
+				{
+					ucstring title;
+					STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title);
+					menu->addLineAtIndex(insertionIndex, "["+s+"] " + title, FILTER_TOGGLE, "dyn"+s);
+					menu->setUserGroupLeft(insertionIndex, createMenuCheckBox(FILTER_TOGGLE, "dyn"+s, pi.ChatInput.DynamicChat[i].isListeningWindow(cw)));
+					++insertionIndex;
+				}
+			}
 		}
 
 
@@ -2907,6 +2936,14 @@ class CHandlerChatSourceSelected : public IActionHandler
 				}
 			}
 		}
+		else 
+		if (nlstricmp(sParams.substr(0, 3), "dyn") == 0)
+		{
+			uint8 i = 0;
+			fromString(sParams.substr(3), i);
+			if (ci.DynamicChat[i].isListeningWindow(cw)) ci.DynamicChat[i].removeListeningWindow(cw);
+			else ci.DynamicChat[i].addListeningWindow(cw);
+		}
 	}
 };
 REGISTER_ACTION_HANDLER( CHandlerChatSourceSelected, "chat_source_selected");
diff --git a/code/ryzom/client/src/net_manager.cpp b/code/ryzom/client/src/net_manager.cpp
index 46c7bbed9..e80aa99bd 100644
--- a/code/ryzom/client/src/net_manager.cpp
+++ b/code/ryzom/client/src/net_manager.cpp
@@ -666,7 +666,7 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c
 		string entry="UI:SAVE:CHAT:COLORS:";
 		switch(mode)
 		{
-		case CChatGroup::dyn_chat:	// dyn_chat takes the color of say
+		case CChatGroup::dyn_chat:	entry+="DYN";	break;
 		case CChatGroup::say:	entry+="SAY";	break;
 		case CChatGroup::shout:	entry+="SHOUT";	break;
 		case CChatGroup::team:	entry+="GROUP";	break;
@@ -762,10 +762,14 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c
 			// retrieve the DBIndex from the dynamic chat id
 			sint32	dbIndex= ChatMngr.getDynamicChannelDbIndexFromId(dynChatId);
 			// if found, display, else discarded
-			if(dbIndex>=0 && dbIndex<CChatGroup::MaxDynChanPerPlayer)
+			if(dbIndex >= 0 && dbIndex < CChatGroup::MaxDynChanPerPlayer)
+			{
 				PeopleInterraction.ChatInput.DynamicChat[dbIndex].displayMessage(finalString, col, 2, &windowVisible);
+			}
 			else
+			{
 				nlwarning("Dynamic chat %s not found for message: %s", dynChatId.toString().c_str(), finalString.toString().c_str());
+			}
 		}
 		else
 		{