Index: src/game/Creature.h =================================================================== --- src/game/Creature.h (revision 4577) +++ src/game/Creature.h (working copy) @@ -203,6 +203,13 @@ char const* ScriptName; }; +struct CreatureLocale +{ + uint32 Entry; + std::string Name[8]; + std::string SubName[8]; +}; + struct EquipmentInfo { uint32 entry; Index: src/game/GameObject.h =================================================================== --- src/game/GameObject.h (revision 4577) +++ src/game/GameObject.h (working copy) @@ -68,6 +68,12 @@ char *ScriptName; }; +struct GameObjectLocale +{ + uint32 Entry; + std::string Name[8]; +}; + // from `ganeobject` struct GameObjectData { Index: src/game/GossipDef.cpp =================================================================== --- src/game/GossipDef.cpp (revision 4577) +++ src/game/GossipDef.cpp (working copy) @@ -135,7 +135,17 @@ data << questID; data << uint32( pQuestMenu->GetItem(iI).m_qIcon ); data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 ); - data << (pQuest ? pQuest->GetTitle() : ""); + std::string Title = pQuest->GetTitle(); + if (pSession->GetSessionLanguage()>0) + { + QuestLocale const *ql = objmgr.GetQuestLocale(questID); + if (ql) + { + if (ql->Title[pSession->GetSessionLanguage()]!="") + Title=ql->Title[pSession->GetSessionLanguage()]; + } + } + data << Title; } pSession->SendPacket( &data ); @@ -178,34 +188,54 @@ data << uint32( 0 ); data << "Greetings $N"; data << "Greetings $N"; - } else + } else { + std::string Text_0[8],Text_1[8]; + for (int i=0;i<8;i++) + { + Text_0[i]=pGossip->Options[i].Text_0; + Text_1[i]=pGossip->Options[i].Text_1; + } - for (int i=0; i<8; i++) - { - data << pGossip->Options[i].Probability; + if (pSession->GetSessionLanguage()>0) + { + NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID); + if (nl) + { + for (int i=0;i<8;i++) + { + if (nl->Text_0[i][pSession->GetSessionLanguage()]!="") + Text_0[i]=nl->Text_0[i][pSession->GetSessionLanguage()]; + if (nl->Text_1[i][pSession->GetSessionLanguage()]!="") + Text_1[i]=nl->Text_1[i][pSession->GetSessionLanguage()]; + } + } + } + for (int i=0; i<8; i++) + { + data << pGossip->Options[i].Probability; - if ( pGossip->Options[i].Text_0 == "" ) - data << pGossip->Options[i].Text_1; - else - data << pGossip->Options[i].Text_0; + if ( Text_0[i] == "" ) + data << Text_1[i]; + else + data << Text_0[i]; - if ( pGossip->Options[i].Text_1 == "" ) - data << pGossip->Options[i].Text_0; - else - data << pGossip->Options[i].Text_1; + if ( Text_1[i] == "" ) + data << Text_0[i]; + else + data << Text_1[i]; - data << pGossip->Options[i].Language; + data << pGossip->Options[i].Language; - data << pGossip->Options[i].Emotes[0]._Delay; - data << pGossip->Options[i].Emotes[0]._Emote; + data << pGossip->Options[i].Emotes[0]._Delay; + data << pGossip->Options[i].Emotes[0]._Emote; - data << pGossip->Options[i].Emotes[1]._Delay; - data << pGossip->Options[i].Emotes[1]._Emote; + data << pGossip->Options[i].Emotes[1]._Delay; + data << pGossip->Options[i].Emotes[1]._Emote; - data << pGossip->Options[i].Emotes[2]._Delay; - data << pGossip->Options[i].Emotes[2]._Emote; + data << pGossip->Options[i].Emotes[2]._Delay; + data << pGossip->Options[i].Emotes[2]._Emote; + } } - pSession->SendPacket( &data ); sLog.outDetail( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " ); @@ -308,9 +338,28 @@ { WorldPacket data(SMSG_QUESTGIVER_QUEST_DETAILS, 100); // guess size + std::string Title,Details,Objectives,EndText; + Title = pQuest->GetTitle(); + Details = pQuest->GetDetails(); + Objectives = pQuest->GetObjectives(); + EndText = pQuest->GetEndText(); + if (pSession->GetSessionLanguage()>0) + { + QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId()); + if (ql) + { + if (ql->Title[pSession->GetSessionLanguage()]!="") + Title=ql->Title[pSession->GetSessionLanguage()]; + if (ql->Details[pSession->GetSessionLanguage()]!="") + Details=ql->Details[pSession->GetSessionLanguage()]; + if (ql->Objectives[pSession->GetSessionLanguage()]!="") + Objectives=ql->Objectives[pSession->GetSessionLanguage()]; + } + } + data << npcGUID; - data << pQuest->GetQuestId() << pQuest->GetTitle() << pQuest->GetDetails(); - data << pQuest->GetObjectives(); + data << pQuest->GetQuestId() << Title << Details; + data << Objectives; data << (uint32)ActivateAccept; data << pQuest->GetSuggestedPlayers(); @@ -379,6 +428,31 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) { + std::string Title,Details,Objectives,EndText; + std::string ObjectiveText[QUEST_OBJECTIVES_COUNT]; + Title = pQuest->GetTitle(); + Details = pQuest->GetDetails(); + Objectives = pQuest->GetObjectives(); + EndText = pQuest->GetEndText(); + for (int i=0;iObjectiveText[i]; + if (pSession->GetSessionLanguage()>0) + { + QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId()); + if (ql) + { + if (ql->Title[pSession->GetSessionLanguage()]!="") + Title=ql->Title[pSession->GetSessionLanguage()]; + if (ql->Details[pSession->GetSessionLanguage()]!="") + Details=ql->Details[pSession->GetSessionLanguage()]; + if (ql->Objectives[pSession->GetSessionLanguage()]!="") + Objectives=ql->Objectives[pSession->GetSessionLanguage()]; + for (int i=0;iObjectiveText[i][pSession->GetSessionLanguage()]!="") + ObjectiveText[i]=ql->ObjectiveText[i][pSession->GetSessionLanguage()]; + } + } + WorldPacket data( SMSG_QUEST_QUERY_RESPONSE, 100 ); // guess size data << uint32(pQuest->GetQuestId()); @@ -439,10 +513,10 @@ data << pQuest->GetPointY(); data << pQuest->GetPointOpt(); - data << pQuest->GetTitle(); - data << pQuest->GetObjectives(); - data << pQuest->GetDetails(); - data << pQuest->GetEndText(); + data << Title; + data << Objectives; + data << Details; + data << EndText; for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++) { @@ -461,7 +535,7 @@ } for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++) - data << pQuest->ObjectiveText[iI]; + data << ObjectiveText[iI]; pSession->SendPacket( &data ); //sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() ); @@ -473,12 +547,27 @@ if(!qInfo) return; + std::string Title,OfferRewardText; + Title = qInfo->GetTitle(); + OfferRewardText = qInfo->GetOfferRewardText(); + if (pSession->GetSessionLanguage()>0) + { + QuestLocale const *ql = objmgr.GetQuestLocale(quest_id); + if (ql) + { + if (ql->Title[pSession->GetSessionLanguage()]!="") + Title=ql->Title[pSession->GetSessionLanguage()]; + if (ql->OfferRewardText[pSession->GetSessionLanguage()]!="") + OfferRewardText=ql->OfferRewardText[pSession->GetSessionLanguage()]; + } + } + WorldPacket data( SMSG_QUESTGIVER_OFFER_REWARD, 50 ); // guess size data << npcGUID; data << quest_id; - data << qInfo->GetTitle(); - data << qInfo->GetOfferRewardText(); + data << Title; + data << OfferRewardText; data << uint32( EnbleNext ); @@ -563,18 +652,33 @@ // items. Otherwise, we'll skip straight to the OfferReward // We may wish a better check, perhaps checking the real quest requirements - if (strlen(pQuest->GetRequestItemsText()) == 0) + if (pQuest->GetRequestItemsText().size() == 0) { SendQuestGiverOfferReward(pQuest->GetQuestId(), npcGUID, true); return; } + std::string Title,RequestItemsText; + Title = pQuest->GetTitle(); + RequestItemsText = pQuest->GetRequestItemsText(); + if (pSession->GetSessionLanguage()>0) + { + QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId()); + if (ql) + { + if (ql->Title[pSession->GetSessionLanguage()]!="") + Title=ql->Title[pSession->GetSessionLanguage()]; + if (ql->RequestItemsText[pSession->GetSessionLanguage()]!="") + RequestItemsText=ql->RequestItemsText[pSession->GetSessionLanguage()]; + } + } + WorldPacket data( SMSG_QUESTGIVER_REQUEST_ITEMS, 50 ); // guess size data << npcGUID; data << pQuest->GetQuestId(); - data << pQuest->GetTitle(); + data << Title; - data << pQuest->GetRequestItemsText(); + data << RequestItemsText; data << uint32(0x00); // unk Index: src/game/ItemHandler.cpp =================================================================== --- src/game/ItemHandler.cpp (revision 4577) +++ src/game/ItemHandler.cpp (working copy) @@ -203,6 +203,20 @@ if( pProto ) { + std::string Name, Description; + Name = pProto->Name1; + Description = pProto->Description; + if (GetSessionLanguage()>0) + { + ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); + if (il) + { + if (il->Name[GetSessionLanguage()]!="") // default to english + Name = il->Name[GetSessionLanguage()]; + if (il->Description[GetSessionLanguage()]!="") // default to english + Description = il->Description[GetSessionLanguage()]; + } + } data << pProto->ItemId; data << pProto->Class; @@ -210,7 +224,7 @@ data << uint32(pProto->Class==ITEM_CLASS_CONSUMABLE ? 0 : pProto->SubClass); data << uint32(-1); // new 2.0.3, not exist in wdb cache? - data << pProto->Name1; + data << Name; data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name... data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00); data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00); @@ -308,7 +322,7 @@ } } data << pProto->Bonding; - data << pProto->Description; + data << Description; data << pProto->PageText; data << pProto->LanguageID; data << pProto->PageMaterial; @@ -812,10 +826,21 @@ ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid ); if( pProto ) { + std::string Name; + Name = pProto->Name1; + if (GetSessionLanguage()>0) + { + ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); + if (il) + { + if (il->Name[GetSessionLanguage()]!="") // default to english + Name = il->Name[GetSessionLanguage()]; + } + } // guess size WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10)); data << pProto->ItemId; - data << pProto->Name1; + data << Name; SendPacket(&data); return; } Index: src/game/ItemPrototype.h =================================================================== --- src/game/ItemPrototype.h (revision 4577) +++ src/game/ItemPrototype.h (working copy) @@ -523,6 +523,13 @@ uint32 FoodType; }; +struct ItemLocale +{ + uint32 Entry; + std::string Name[8]; + std::string Description[8]; +}; + // GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform #if defined( __GNUC__ ) #pragma pack() Index: src/game/NPCHandler.h =================================================================== --- src/game/NPCHandler.h (revision 4577) +++ src/game/NPCHandler.h (working copy) @@ -34,6 +34,12 @@ uint32 Next_Page; }; +struct PageTextLocale +{ + uint32 Entry; + std::string Text[8]; +}; + // GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform #if defined( __GNUC__ ) #pragma pack() @@ -61,4 +67,12 @@ uint32 Text_ID; GossipTextOption Options[8]; }; + +struct NpcTextLocale +{ + uint32 Entry; + std::string Text_0[8][8]; + std::string Text_1[8][8]; +}; + #endif Index: src/game/ObjectMgr.cpp =================================================================== --- src/game/ObjectMgr.cpp (revision 4577) +++ src/game/ObjectMgr.cpp (working copy) @@ -386,6 +386,59 @@ return sCreatureStorage.LookupEntry(id); } +void ObjectMgr::LoadCreatureLocales() +{ + const World::LocalizationMap *Locales = sWorld.GetSupportedLocals(); + + World::LocalizationMap::const_iterator itr = Locales->begin(); + std::ostringstream ostr; + ostr << "`name_loc" << (int)*itr << "`,`subname_loc" << (int)*itr << "`"; + itr++; + while (itr != Locales->end()) + { + ostr << ",`name_loc" << (int)*itr << "`,`subname_loc" << (int)*itr << "`"; + itr++; + } + + QueryResult *result = sDatabase.PQuery("SELECT `entry`,%s FROM `locales_creature`",ostr.str().c_str()); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + CreatureLocale& data = mCreatureLocaleMap[entry]; + uint16 currentIndex=0; + for (itr = Locales->begin(); itr != Locales->end(); ++itr) + { + data.Name[*itr] = fields[currentIndex*2+1].GetCppString(); + data.SubName[*itr]= fields[currentIndex*2+2].GetCppString(); + currentIndex++; + } + + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() ); +} + void ObjectMgr::LoadCreatureTemplates() { sCreatureStorage.Load(); @@ -913,6 +966,58 @@ sLog.outString(); } +void ObjectMgr::LoadItemLocales() +{ + const World::LocalizationMap *Locales = sWorld.GetSupportedLocals(); + + World::LocalizationMap::const_iterator itr = Locales->begin(); + std::ostringstream ostr; + ostr << "`name_loc" << (int)*itr << "`,`description_loc" << (int)*itr << "`"; + itr++; + while (itr != Locales->end()) + { + ostr << ",`name_loc" << (int)*itr << "`,`description_loc" << (int)*itr << "`"; + itr++; + } + + QueryResult *result = sDatabase.PQuery("SELECT `entry`,%s FROM `locales_item`",ostr.str().c_str()); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 Item locale strings. DB table `locales_item` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + ItemLocale& data = mItemLocaleMap[entry]; + uint16 currentIndex=0; + for (itr = Locales->begin(); itr != Locales->end(); ++itr) + { + data.Name[*itr] = fields[currentIndex*2+1].GetCppString(); + data.Description[*itr]= fields[currentIndex*2+2].GetCppString(); + currentIndex++; + } + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() ); +} + void ObjectMgr::LoadItemPrototypes() { sItemStorage.Load (); @@ -2133,6 +2238,69 @@ sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() ); } +void ObjectMgr::LoadQuestLocales() +{ + const World::LocalizationMap *Locales = sWorld.GetSupportedLocals(); + + World::LocalizationMap::const_iterator itr = Locales->begin(); + std::ostringstream ostr; + ostr << "`Title_loc" << (int)*itr << "`,`Details_loc" << (int)*itr << "`,`Objectives_loc" << (int)*itr; + ostr << "`,`OfferRewardText_loc" << (int)*itr << "`,`RequestItemsText_loc" << (int)*itr << "`,`EndText_loc" << (int)*itr; + ostr << "`,`ObjectiveText1_loc" << (int)*itr << "`,`ObjectiveText2_loc" << (int)*itr << "`,`ObjectiveText3_loc" << (int)*itr << "`,`ObjectiveText4_loc" << (int)*itr << "`"; + + itr++; + while (itr != Locales->end()) + { + ostr << ",`Title_loc" << (int)*itr << "`,`Details_loc" << (int)*itr << "`,`Objectives_loc" << (int)*itr; + ostr << "`,`OfferRewardText_loc" << (int)*itr << "`,`RequestItemsText_loc" << (int)*itr << "`,`EndText_loc" << (int)*itr; + ostr << "`,`ObjectiveText1_loc" << (int)*itr << "`,`ObjectiveText2_loc" << (int)*itr << "`,`ObjectiveText3_loc" << (int)*itr << "`,`ObjectiveText4_loc" << (int)*itr << "`"; + itr++; + } + + QueryResult *result = sDatabase.PQuery("SELECT `entry`,%s FROM `locales_quest`",ostr.str().c_str()); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + QuestLocale& data = mQuestLocaleMap[entry]; + uint16 currentIndex=0; + for (itr = Locales->begin(); itr != Locales->end(); ++itr) + { + data.Title[*itr] = fields[currentIndex*10+1].GetCppString(); + data.Details[*itr] = fields[currentIndex*10+2].GetCppString(); + data.Objectives[*itr] = fields[currentIndex*10+3].GetCppString(); + data.OfferRewardText[*itr] = fields[currentIndex*10+4].GetCppString(); + data.RequestItemsText[*itr] = fields[currentIndex*10+5].GetCppString(); + data.EndText[*itr] = fields[currentIndex*10+2].GetCppString(); + for (int i=0;i<4;i++) + data.ObjectiveText[i][*itr] = fields[currentIndex*10+6+i].GetCppString(); + currentIndex++; + } + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() ); +} + void ObjectMgr::LoadSpellChains() { mSpellChains.clear(); // need for reload case @@ -2716,6 +2884,58 @@ sLog.outString(); } +void ObjectMgr::LoadPageTextLocales() +{ + const World::LocalizationMap *Locales = sWorld.GetSupportedLocals(); + + World::LocalizationMap::const_iterator itr = Locales->begin(); + std::ostringstream ostr; + ostr << "`text_loc" << (int)*itr << "`"; + itr++; + while (itr != Locales->end()) + { + ostr << ",`text_loc" << (int)*itr << "`"; + itr++; + } + + QueryResult *result = sDatabase.PQuery("SELECT `entry`,%s FROM `locales_page_text`",ostr.str().c_str()); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + PageTextLocale& data = mPageTextLocaleMap[entry]; + uint16 currentIndex=0; + for (itr = Locales->begin(); itr != Locales->end(); ++itr) + { + data.Text[*itr] = fields[currentIndex+1].GetCppString(); + currentIndex++; + } + + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() ); +} + void ObjectMgr::AddGossipText(GossipText *pGText) { ASSERT( pGText->Text_ID ); @@ -2794,6 +3014,64 @@ delete result; } +void ObjectMgr::LoadNpcTextLocales() +{ + const World::LocalizationMap *Locales = sWorld.GetSupportedLocals(); + + World::LocalizationMap::const_iterator itr = Locales->begin(); + std::ostringstream ostr; + ostr << "`Text0_0_loc" << (int)*itr << "`,`Text0_1_loc" << (int)*itr << "`"; + for (int i=1;i<8;i++) + ostr << ",`Text" << i << "_0_loc" << (int)*itr << "`,`Text" << i << "_1_loc" << (int)*itr << "`"; + itr++; + while (itr != Locales->end()) + { + for (int i=0;i<8;i++) + ostr << ",`Text" << i << "_0_loc" << (int)*itr << "`,`Text" << i << "_1_loc" << (int)*itr << "`"; + itr++; + } + + QueryResult *result = sDatabase.PQuery("SELECT `entry`,%s FROM `locales_npc_text`",ostr.str().c_str()); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + NpcTextLocale& data = mNpcTextLocaleMap[entry]; + uint16 currentIndex=0; + for (itr = Locales->begin(); itr != Locales->end(); ++itr) + { + for (int i=0;i<8;i++) + { + data.Text_0[i][*itr] = fields[currentIndex*16+1+i*2].GetCppString(); + data.Text_1[i][*itr] = fields[currentIndex*16+2+i*2].GetCppString(); + } + currentIndex++; + } + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() ); +} + //not very fast function but it is called only once a day, or on starting-up void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) { @@ -3513,6 +3791,58 @@ return 0; } +void ObjectMgr::LoadGameObjectLocales() +{ + const World::LocalizationMap *Locales = sWorld.GetSupportedLocals(); + + World::LocalizationMap::const_iterator itr = Locales->begin(); + std::ostringstream ostr; + ostr << "`name_loc" << (int)*itr << "`"; + itr++; + while (itr != Locales->end()) + { + ostr << ",`name_loc" << (int)*itr << "`"; + itr++; + } + + QueryResult *result = sDatabase.PQuery("SELECT `entry`,%s FROM `locales_gameobject`",ostr.str().c_str()); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + GameObjectLocale& data = mGameObjectLocaleMap[entry]; + uint16 currentIndex=0; + for (itr = Locales->begin(); itr != Locales->end(); ++itr) + { + data.Name[*itr] = fields[currentIndex+1].GetCppString(); + currentIndex++; + } + + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() ); +} + void ObjectMgr::LoadGameobjectInfo() { sGOStorage.Load(); Index: src/game/ObjectMgr.h =================================================================== --- src/game/ObjectMgr.h (revision 4577) +++ src/game/ObjectMgr.h (working copy) @@ -112,6 +112,12 @@ typedef HM_NAMESPACE::hash_map CreatureDataMap; typedef HM_NAMESPACE::hash_map GameObjectDataMap; +typedef HM_NAMESPACE::hash_map CreatureLocaleMap; +typedef HM_NAMESPACE::hash_map GameObjectLocaleMap; +typedef HM_NAMESPACE::hash_map ItemLocaleMap; +typedef HM_NAMESPACE::hash_map QuestLocaleMap; +typedef HM_NAMESPACE::hash_map NpcTextLocaleMap; +typedef HM_NAMESPACE::hash_map PageTextLocaleMap; typedef std::multimap QuestRelations; @@ -415,17 +421,23 @@ void LoadSpellScriptTarget(); void LoadPetCreateSpells(); + void LoadCreatureLocales(); void LoadCreatureTemplates(); void LoadCreatures(); void LoadCreatureRespawnTimes(); void LoadCreatureAddons(); void LoadCreatureModelInfo(); void LoadEquipmentTemplates(); + void LoadGameObjectLocales(); void LoadGameobjects(); void LoadGameobjectRespawnTimes(); void LoadSpellProcEvents(); void LoadSpellThreats(); void LoadItemPrototypes(); + void LoadItemLocales(); + void LoadQuestLocales(); + void LoadNpcTextLocales(); + void LoadPageTextLocales(); void LoadGossipText(); @@ -617,6 +629,43 @@ CreatureData& NewCreatureData(uint32 guid) { return mCreatureDataMap[guid]; } void DeleteCreatureData(uint32 guid); + CreatureLocale const* GetCreatureLocale(uint32 entry) const + { + CreatureLocaleMap::const_iterator itr = mCreatureLocaleMap.find(entry); + if(itr==mCreatureLocaleMap.end()) return NULL; + return &itr->second; + } + GameObjectLocale const* GetGameObjectLocale(uint32 entry) const + { + GameObjectLocaleMap::const_iterator itr = mGameObjectLocaleMap.find(entry); + if(itr==mGameObjectLocaleMap.end()) return NULL; + return &itr->second; + } + ItemLocale const* GetItemLocale(uint32 entry) const + { + ItemLocaleMap::const_iterator itr = mItemLocaleMap.find(entry); + if(itr==mItemLocaleMap.end()) return NULL; + return &itr->second; + } + QuestLocale const* GetQuestLocale(uint32 entry) const + { + QuestLocaleMap::const_iterator itr = mQuestLocaleMap.find(entry); + if(itr==mQuestLocaleMap.end()) return NULL; + return &itr->second; + } + NpcTextLocale const* GetNpcTextLocale(uint32 entry) const + { + NpcTextLocaleMap::const_iterator itr = mNpcTextLocaleMap.find(entry); + if(itr==mNpcTextLocaleMap.end()) return NULL; + return &itr->second; + } + PageTextLocale const* GetPageTextLocale(uint32 entry) const + { + PageTextLocaleMap::const_iterator itr = mPageTextLocaleMap.find(entry); + if(itr==mPageTextLocaleMap.end()) return NULL; + return &itr->second; + } + GameObjectData const* GetGOData(uint32 guid) const { GameObjectDataMap::const_iterator itr = mGameObjectDataMap.find(guid); @@ -721,7 +770,13 @@ MapObjectGuids mMapObjectGuids; CreatureDataMap mCreatureDataMap; + CreatureLocaleMap mCreatureLocaleMap; GameObjectDataMap mGameObjectDataMap; + GameObjectLocaleMap mGameObjectLocaleMap; + ItemLocaleMap mItemLocaleMap; + QuestLocaleMap mQuestLocaleMap; + NpcTextLocaleMap mNpcTextLocaleMap; + PageTextLocaleMap mPageTextLocaleMap; RespawnTimes mCreatureRespawnTimes; RespawnTimes mGORespawnTimes; Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 4577) +++ src/game/Player.cpp (working copy) @@ -10447,6 +10447,15 @@ title = gossiptext->Options[0].Text_0 == "" ? gossiptext->Options[0].Text_1 : gossiptext->Options[0].Text_0; if( &title == NULL ) title = ""; + if (GetSession()->GetSessionLanguage()>0) + { + NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid); + if (nl) + { + if (nl->Text_0[0][GetSession()->GetSessionLanguage()]!="" || nl->Text_1[0][GetSession()->GetSessionLanguage()]!="") + title = nl->Text_0[0][GetSession()->GetSessionLanguage()]=="" ? nl->Text_1[0][GetSession()->GetSessionLanguage()] : nl->Text_0[0][GetSession()->GetSessionLanguage()]; + } + } } } PlayerTalkClass->SendQuestGiverQuestList( qe, title, guid ); Index: src/game/QueryHandler.cpp =================================================================== --- src/game/QueryHandler.cpp (revision 4577) +++ src/game/QueryHandler.cpp (working copy) @@ -114,8 +114,6 @@ // return; } - //CreatureInfo const *ci = unit->GetCreatureInfo(); - CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry); if (!ci) @@ -124,18 +122,38 @@ return; } + std::string Name, SubName; + //CreatureInfo const *ci = unit->GetCreatureInfo(); + Name = ci->Name; + SubName = ci->SubName; + + if (GetSessionLanguage()>0) + { + CreatureLocale const *cl = objmgr.GetCreatureLocale(entry); + if (cl) + { + if (cl->Name[GetSessionLanguage()]!="") // default to english + Name = cl->Name[GetSessionLanguage()]; + if (cl->SubName[GetSessionLanguage()]!="") // same + SubName = cl->SubName[GetSessionLanguage()]; + } + } + sLog.outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u - GUID: %u.", ci->Name, entry, guid); WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 100 ); // guess size data << (uint32)entry; // creature entry - data << (unit ? unit->GetName() : ci->Name); // creature name + if (unit) + data << ((unit->isPet()) ? unit->GetName() : Name); // creature name + else + data << Name; data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty if (unit) - data << ((unit->isPet()) ? "Pet" : ci->SubName); // subname + data << ((unit->isPet()) ? "Pet" : SubName); // subname else - data << ci->SubName; + data << SubName; data << ci->flag1; // flags wdbFeild7=wad flags1 @@ -157,7 +175,7 @@ data << (float)1; // unk data << (float)1; // unk data << uint8(ci->civilian); - data << uint8(0); // boss flag? + data << uint8(ci->RacialLeader); SendPacket( &data ); } @@ -171,6 +189,7 @@ recv_data >> entryID; recv_data >> guid; + std::string Name; const GameObjectInfo *info = objmgr.GetGameObjectInfo(entryID); // guess size WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 150 ); @@ -181,11 +200,21 @@ } else { + Name = info->name; + if (GetSessionLanguage()>0) + { + GameObjectLocale const *gl = objmgr.GetGameObjectLocale(entryID); + if (gl) + { + if (gl->Name[GetSessionLanguage()]!="") // default to english + Name = gl->Name[GetSessionLanguage()]; + } + } sLog.outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u - GUID: %u.", info->name, entryID, guid); data << entryID; data << (uint32)info->type; data << (uint32)info->displayId; - data << info->name; + data << Name; data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4 data << uint8(0); // 2.0.3, string data << uint8(0); // 2.0.3, string @@ -266,32 +295,54 @@ data << uint32( 0 ); data << "Greetings $N"; data << "Greetings $N"; - } else + } else { + std::string Text_0[8], Text_1[8]; + for (int i=0;i<8;i++) + { + Text_0[i]=pGossip->Options[i].Text_0; + Text_1[i]=pGossip->Options[i].Text_1; + } - for (int i=0; i<8; i++) - { - data << pGossip->Options[i].Probability; + if (GetSessionLanguage()>0) + { + NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID); + if (nl) + { + for (int i=0;i<8;i++) + { + if (nl->Text_0[i][GetSessionLanguage()]!="") + Text_0[i]=nl->Text_0[i][GetSessionLanguage()]; + if (nl->Text_1[i][GetSessionLanguage()]!="") + Text_1[i]=nl->Text_1[i][GetSessionLanguage()]; + } + } + } - if ( pGossip->Options[i].Text_0 == "" ) - data << pGossip->Options[i].Text_1; - else - data << pGossip->Options[i].Text_0; + for (int i=0; i<8; i++) + { + data << pGossip->Options[i].Probability; - if ( pGossip->Options[i].Text_1 == "" ) - data << pGossip->Options[i].Text_0; - else - data << pGossip->Options[i].Text_1; + if ( Text_0[i] == "" ) + data << Text_1[i]; + else + data << Text_0[i]; - data << pGossip->Options[i].Language; + if ( Text_1[i] == "" ) + data << Text_0[i]; + else + data << Text_1[i]; - data << pGossip->Options[i].Emotes[0]._Delay; - data << pGossip->Options[i].Emotes[0]._Emote; + data << pGossip->Options[i].Language; - data << pGossip->Options[i].Emotes[1]._Delay; - data << pGossip->Options[i].Emotes[1]._Emote; + data << pGossip->Options[i].Emotes[0]._Delay; + data << pGossip->Options[i].Emotes[0]._Emote; - data << pGossip->Options[i].Emotes[2]._Delay; - data << pGossip->Options[i].Emotes[2]._Emote; + data << pGossip->Options[i].Emotes[1]._Delay; + data << pGossip->Options[i].Emotes[1]._Emote; + + data << pGossip->Options[i].Emotes[2]._Delay; + data << pGossip->Options[i].Emotes[2]._Emote; + } } SendPacket( &data ); @@ -323,7 +374,17 @@ } else { - data << pPage->Text; + std::string Text = pPage->Text; + if (GetSessionLanguage()>0) + { + PageTextLocale const *pl = objmgr.GetPageTextLocale(pageID); + if (pl) + { + if (pl->Text[GetSessionLanguage()]!="") + Text = pl->Text[GetSessionLanguage()]; + } + } + data << Text; data << uint32(pPage->Next_Page); pageID = pPage->Next_Page; } Index: src/game/QuestDef.h =================================================================== --- src/game/QuestDef.h (revision 4577) +++ src/game/QuestDef.h (working copy) @@ -126,6 +126,18 @@ //QUEST_SPECIAL_FLAGS_UNK5 = 2048, // unknown tbc }; +struct QuestLocale +{ + uint32 Entry; + std::string Title[8]; + std::string Details[8]; + std::string Objectives[8]; + std::string OfferRewardText[8]; + std::string RequestItemsText[8]; + std::string EndText[8]; + std::string ObjectiveText[QUEST_OBJECTIVES_COUNT][8]; +}; + // This Quest class provides a convenient way to access a few pretotaled (cached) quest details, // all base quest information, and any utility functions such as generating the amount of // xp to give @@ -147,9 +159,9 @@ uint32 GetRequiredRaces() const { return RequiredRaces; } uint32 GetRequiredSkillValue() const { return RequiredSkillValue; } uint32 GetRequiredMinRepFaction() const { return RequiredMinRepFaction; } - int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; } + int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; } uint32 GetRequiredMaxRepFaction() const { return RequiredMaxRepFaction; } - int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; } + int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; } uint32 GetSuggestedPlayers() const { return SuggestedPlayers; } uint32 GetLimitTime() const { return LimitTime; } int32 GetNextQuestId() const { return NextQuestId; } @@ -158,12 +170,12 @@ uint32 GetSrcItemId() const { return SrcItemId; } uint32 GetSrcItemCount() const { return SrcItemCount; } uint32 GetSrcSpell() const { return SrcSpell; } - const char* GetTitle() const { return Title.c_str(); } - const char* GetDetails() const { return Details.c_str(); } - const char* GetObjectives() const { return Objectives.c_str(); } - const char* GetOfferRewardText() const { return OfferRewardText.c_str(); } - const char* GetRequestItemsText() const { return RequestItemsText.c_str(); } - const char* GetEndText() const { return EndText.c_str(); } + std::string GetTitle() const { return Title; } + std::string GetDetails() const { return Details; } + std::string GetObjectives() const { return Objectives; } + std::string GetOfferRewardText() const { return OfferRewardText; } + std::string GetRequestItemsText() const { return RequestItemsText; } + std::string GetEndText() const { return EndText; } int32 GetRewOrReqMoney() const { return RewOrReqMoney; } uint32 GetRewXpOrMoney() const { return RewXpOrMoney; } uint32 GetRewSpell() const { return RewSpell; } Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 4577) +++ src/game/World.cpp (working copy) @@ -475,6 +475,9 @@ exit(1); } + std::string localization = sConfig.GetStringDefault("StringLocalize", ""); + FillSupportedLocals(localization); + ///- Update the realm entry in the database with the realm type from the config file //No SQL injection as values are treated as integers loginDatabase.PExecute("UPDATE `realmlist` SET `icon` = %u WHERE `id` = '%d'", m_configs[CONFIG_GAME_TYPE],realmID); @@ -495,6 +498,17 @@ objmgr.PackInstances(); ///- Load static data tables from the database + if (m_SupportedLocals.size()>0) + { + sLog.outString( "Loading Localization strings..." ); + objmgr.LoadCreatureLocales(); + objmgr.LoadGameObjectLocales(); + objmgr.LoadItemLocales(); + objmgr.LoadQuestLocales(); + objmgr.LoadNpcTextLocales(); + objmgr.LoadPageTextLocales(); + } + sLog.outString( "Loading Game Object Templates..." ); objmgr.LoadGameobjectInfo(); @@ -1647,3 +1661,29 @@ loginDatabase.PExecute("INSERT INTO `realmcharacters` (`numchars`, `acctid`, `realmid`) VALUES (%u, %u, %u) ON DUPLICATE KEY UPDATE `numchars` = '%u'", charCount, accountId, realmID, charCount); } } + +void World::FillSupportedLocals(std::string str) +{ + if(str=="") + { + return; // English by default if none selected + } + int local; + std::istringstream ss(str); + for(int i = 0; i < 8; ++i) + { + ss >> local; + if (local > 0 && local <8) + SetSupportedLocals((uint8) local); + if(!ss) + break; + } + if (m_SupportedLocals.empty()) + { + sLog.outError("Unable to determine your Locales!!"); + exit(1); + } + LocalizationMap::const_iterator itr; + for (itr = m_SupportedLocals.begin(); itr != m_SupportedLocals.end(); ++itr) + sLog.outString("Support for local %u will be used",*itr); +} Index: src/game/World.h =================================================================== --- src/game/World.h (revision 4577) +++ src/game/World.h (working copy) @@ -349,11 +349,16 @@ void UpdateRealmCharCount(uint32 accid); + typedef std::set LocalizationMap; + LocalizationMap const* GetSupportedLocals() const { return &m_SupportedLocals; } + protected: void _UpdateGameTime(); void ScriptsProcess(); // callback for UpdateRealmCharacters void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId); + void SetSupportedLocals(uint8 local) { m_SupportedLocals.insert(local); } + void FillSupportedLocals(std::string str); private: time_t m_startTime; @@ -394,6 +399,9 @@ // CLI command holder to be thread safe ZThread::LockedQueue cliCmdQueue; SqlResultQueue *m_resultQueue; + + // Localization + LocalizationMap m_SupportedLocals; }; extern uint32 realmID; Index: src/game/WorldSession.cpp =================================================================== --- src/game/WorldSession.cpp (revision 4577) +++ src/game/WorldSession.cpp (working copy) @@ -40,9 +40,9 @@ #include "Chat.h" /// WorldSession constructor -WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time) : +WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, uint8 locale) : LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time), -_player(NULL), _socket(sock),_security(sec), _accountId(id), m_isTBC(tbc), +_player(NULL), _socket(sock),_security(sec), _accountId(id), m_isTBC(tbc), m_sessionLanguage(locale), _logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false) { FillOpcodeHandlerHashTable(); Index: src/game/WorldSession.h =================================================================== --- src/game/WorldSession.h (revision 4577) +++ src/game/WorldSession.h (working copy) @@ -64,7 +64,7 @@ { friend class CharacterHandler; public: - WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time); + WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, uint8 locale); ~WorldSession(); bool PlayerLoading() { return m_playerLoading; } @@ -170,6 +170,9 @@ // Account mute time time_t m_muteTime; + + // Locales + uint8 GetSessionLanguage() { return m_sessionLanguage; } protected: void HandleCharEnumOpcode(WorldPacket& recvPacket); @@ -556,6 +559,7 @@ bool m_playerLoading; // code processed in LoginPlayer bool m_playerLogout; // code processed in LogoutPlayer bool m_playerRecentlyLogout; + uint8 m_sessionLanguage; void FillOpcodeHandlerHashTable(); Index: src/game/WorldSocket.cpp =================================================================== --- src/game/WorldSocket.cpp (revision 4577) +++ src/game/WorldSocket.cpp (working copy) @@ -221,6 +221,7 @@ uint32 unk2; uint32 BuiltNumberClient; uint32 id, security; + uint8 locale; bool tbc = false; std::string account; Sha1Hash sha1; @@ -248,8 +249,8 @@ std::string safe_account=account; // Duplicate, else will screw the SHA hash verification below loginDatabase.escape_string(safe_account); //No SQL injection, username escaped. - // 0 1 2 3 4 5 6 7 8 9 - QueryResult *result = loginDatabase.PQuery("SELECT `id`,`gmlevel`,`sessionkey`,`last_ip`,`locked`, `I`, `v`, `s`, `tbc`,`mutetime` FROM `account` WHERE `username` = '%s'", safe_account.c_str()); + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult *result = loginDatabase.PQuery("SELECT `id`,`gmlevel`,`sessionkey`,`last_ip`,`locked`, `I`, `v`, `s`, `tbc`,`mutetime`,`locale` FROM `account` WHERE `username` = '%s'", safe_account.c_str()); ///- Stop if the account is not found if ( !result ) @@ -321,7 +322,7 @@ security = fields[1].GetUInt16(); K.SetHexStr(fields[2].GetString()); time_t mutetime = time_t(fields[9].GetUInt64()); - + locale = fields[10].GetUInt8(); delete result; ///- Re-check account ban (same check as in realmd) /// TO DO: why on earth do 2 checks for same thing? @@ -395,7 +396,7 @@ SendPacket(&packet); ///- Create a new WorldSession for the player and add it to the World - _session = new WorldSession(id, this,security,tbc,mutetime); + _session = new WorldSession(id, this,security,tbc,mutetime,locale); sWorld.AddSession(_session); if(sLog.IsOutDebug()) // optimize disabled debug output Index: src/mangosd/mangosd.conf.in =================================================================== --- src/mangosd/mangosd.conf.in (revision 4577) +++ src/mangosd/mangosd.conf.in (working copy) @@ -54,6 +54,14 @@ # 255 = Auto Detect (Default) DBC.Locale = 255 +# Full localization support activation +# use the same codes as for DBC language to enable localization support in this language +# Be informed that activating it consumes some memory (around 57MB per language activated) +# ex: StringLocalize = "2" will activate French on top of default English +# You can activate several languages like StringLocalize = "2 3" for French and German +# StringLocalize = "" (Default = Disabled) +StringLocalize = "" + # Enable logging of GM commands - all SQL code will be written to a log file # All commands are written to a file: YYYY-MM-DD_logSQL.sql # If a new day starts (00:00:00) then a new file is created - the old Index: src/realmd/AuthSocket.cpp =================================================================== --- src/realmd/AuthSocket.cpp (revision 4577) +++ src/realmd/AuthSocket.cpp (working copy) @@ -448,6 +448,14 @@ pkt.append(s.AsByteArray(), s.GetNumBytes()); pkt.append(unk3.AsByteArray(), 16); pkt << (uint8)0; // Added in 1.12.x client branch + + QueryResult *localeresult = dbRealmServer.PQuery("SELECT `locale` FROM `localization` WHERE `string` = '%c%c'",ch->country[3],ch->country[2]); + if( localeresult ) + _localization=(*localeresult)[0].GetUInt8(); + else + _localization=0; + + sLog.outBasic("[AuthChallenge] acount %s is using '%c%c' locale (%u)", _login.c_str (), ch->country[3],ch->country[2], _localization); } } delete result; @@ -595,7 +603,7 @@ ///- Update the sessionkey, last_ip and last login time in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket - dbRealmServer.PExecute("UPDATE `account` SET `sessionkey` = '%s', `last_ip` = '%s', `last_login` = NOW() WHERE `username` = '%s'",K.AsHexStr(), GetRemoteAddress().c_str(), _safelogin.c_str() ); + dbRealmServer.PExecute("UPDATE `account` SET `sessionkey` = '%s', `last_ip` = '%s', `last_login` = NOW(), `locale` = '%u' WHERE `username` = '%s'",K.AsHexStr(), GetRemoteAddress().c_str(), _localization, _safelogin.c_str() ); ///- Finish SRP6 and send the final result to the client sha.Initialize(); Index: src/realmd/AuthSocket.h =================================================================== --- src/realmd/AuthSocket.h (revision 4577) +++ src/realmd/AuthSocket.h (working copy) @@ -68,6 +68,7 @@ std::string _login; std::string _safelogin; + uint8 _localization; }; #endif /// @}