Search the Community

Showing results for tags 'mt2 fr c++'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Register & Login
    • Register
    • Login
  • Espace Communautaire
    • Funky Emulation
    • Présentations
    • Services
    • Discussions Générales
    • Bureau de la Communauté
    • Espace Premium
  • Emulation & Co
    • Rewrite a Release / Tutorial
    • Suggest a Release / Tutorial
    • The Ideas Box
  • Emulation de jeux
    • RaiderZ
    • Aura Kingdom
    • Metin2
    • Dofus
    • World of Warcraft
    • Minecraft
    • Aion
    • Habbo
    • Voir plus...
  • Espace Divers
  • Internationnal Forum
  • PassionDev's Forum
  • M2Project - Metin2's M2SF
  • M2Project - Metin2's Aide / Questions / Support
  • M2Project - Metin2's Tutoriels & Partages
  • M2Project - Metin2's Suggestions
  • M2Project - Metin2's Report de Bugs

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Nationality


Sexe


Discord


Skype


Biographie


Site


Nombre

 
or  

Found 68 results

  1. Coucou, Je viens vous montrer comment ajouter un format d'image supplémentaire lisible par votre lanceur. Dans le projet ScriptLib, ouvrez le dossier Resource.cpp et chercher: m_resManager.RegisterResourceNewFunctionPointer("bmp", NewImage); Ajouter en bas le format que vous voulez ajoutez ( donc pour moi png ): m_resManager.RegisterResourceNewFunctionPointer("png", NewImage);
  2. Bonjour, J'ai décidé de refaire ce tutoriel Aujourd'hui je vais vous expliquer comment ajouté l'option "détruire" lorsque vous voulez jeter un item. Ca va être assez long, mais facile si vous suivez bien le tutoriel. Commençons ______________________________________________________________________ PRÉREQUIS ______________________________________________________________________ 1.Des source Serveur 2.Des sources Client 3.Un client ______________________________________________________________________ 1.SOURCE SERVEUR ______________________________________________________________________ Ouvrez votre "packet.h" et recherchez: HEADER_CG_ITEM_DROP2 = 20, Ajoutez ceci juste en dessous: HEADER_CG_ITEM_DESTROY = 21, Recherchez maintenant: typedef struct command_item_drop2 { BYTE header; TItemPos Cell; DWORD gold; BYTE count; } TPacketCGItemDrop2; Ajoutez ceci juste en dessous: typedef struct command_item_destroy { BYTE header; TItemPos Cell; } TPacketCGItemDestroy; Ouvrez maintenant le fichier packet_info.cpp et recherchez: Set(HEADER_CG_ITEM_DROP2, sizeof(TPacketCGItemDrop2), "ItemDrop2", true); Ajoutez ceci juste en dessous: Set(HEADER_CG_ITEM_DESTROY, sizeof(TPacketCGItemDestroy), "ItemDestroy", true); Ouvrez maintenant le fichier input_main.cpp et recherchez la fonction: void CInputMain::ItemDrop2(LPCHARACTER ch, const char * data) Ajoutez cette fonction juste après: void CInputMain::ItemDestroy(LPCHARACTER ch, const char * data) { struct command_item_destroy * pinfo = (struct command_item_destroy *) data; if (ch) ch->DestroyItem(pinfo->Cell); } Cherchez maintenant ceci: case HEADER_CG_ITEM_DROP2: if (!ch->IsObserverMode()) ItemDrop2(ch, c_pData); break; Et ajoutez: case HEADER_CG_ITEM_DESTROY: if (!ch->IsObserverMode()) ItemDestroy(ch, c_pData); break; Ouvrez maintenant le fichier char_item.cpp et recherchez la fonction: bool CHARACTER::DropItem(TItemPos Cell, BYTE bCount) Ajoutez cette fonction juste au-dessus: bool CHARACTER::DestroyItem(TItemPos Cell) { LPITEM item = NULL; if (!CanHandleItem()) { if (NULL != DragonSoul_RefineWindow_GetOpener()) ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°*È*âÀ» ¿¬ »óÅ¿¡¼*´Â ¾ÆÀÌÅÛÀ» ¿Å±æ ¼ö ¾ø½À´Ï´Ù.")); return false; } if (IsDead()) return false; if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell))) return false; if (item->IsExchanging()) return false; if (true == item->isLocked()) return false; if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) return false; if (item->GetCount() <= 0) return false; SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, 255); ITEM_MANAGER::instance().RemoveItem(item); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("L'item %s est desormais detruit."), item->GetName()); return true; } Ouvrez maintenant le fichier char.h et recherchez: bool DropItem(TItemPos Cell, BYTE bCount=0); Ajoutez ceci juste au-dessus: bool DestroyItem(TItemPos Cell); Ouvrez maintenant le fichier input.h et recherchez: void ItemDrop2(LPCHARACTER ch, const char * data); Ajoutez ceci juste en dessous: void ItemDestroy(LPCHARACTER ch, const char * data); ______________________________________________________________________ 2. SOURCE CLIENT ______________________________________________________________________ Ouvrez le fichier packet.h et recherchez: HEADER_CG_ITEM_DROP2 = 20, Ajoutez ceci juste en dessous: HEADER_CG_ITEM_DESTROY = 21, Recherchez maintenant: typedef struct command_item_drop2 { BYTE header; TItemPos pos; DWORD gold; BYTE count; } TPacketCGItemDrop2; Ajoutez ceci juste en dessous: typedef struct command_item_destroy { BYTE header; TItemPos pos; }TPacketCGItemDestroy; Ouvrez maintenant le fichier PythonNetworkStreamPhaseGameItem.cpp et recherchez la fonction: bool CPythonNetworkStream::SendItemDropPacketNew(TItemPos pos, DWORD elk, DWORD count) Ajoutez cette fonction juste en dessous: bool CPythonNetworkStream::SendItemDestroyPacket(TItemPos pos) { if (!__CanActMainInstance()) return true; TPacketCGItemDestroy itemDestroyPacket; itemDestroyPacket.header = HEADER_CG_ITEM_DESTROY; itemDestroyPacket.pos = pos; if (!Send(sizeof(itemDestroyPacket), &itemDestroyPacket)) { Tracen("SendItemDestroyPacket Error"); return false; } return SendSequence(); } Ouvrez maintenant le fichier PythonNetworkStreamModule.cpp et recherchez la fonction: PyObject* netSendItemDropPacket(PyObject* poSelf, PyObject* poArgs) Ajoutez cette fonction juste en dessous: PyObject* netSendItemDestroyPacket(PyObject* poSelf, PyObject* poArgs) { TItemPos Cell; if (!PyTuple_GetInteger(poArgs, 0, &Cell.cell)) return Py_BuildException(); CPythonNetworkStream& rkNetStream = CPythonNetworkStream::Instance(); rkNetStream.SendItemDestroyPacket(Cell); return Py_BuildNone(); } Recherchez maintenant: { "SendItemDropPacketNew", netSendItemDropPacketNew, METH_VARARGS }, Ajoutez ceci juste après: { "SendItemDestroyPacket", netSendItemDestroyPacket, METH_VARARGS }, Ouvrez maintenant le fichier PythonNetworkStream.h et recherchez la fonction: bool SendItemDropPacketNew(TItemPos pos, DWORD elk, DWORD count); Ajoutez ceci juste après: bool SendItemDestroyPacket(TItemPos pos); ______________________________________________________________________ 3. PYTHON CLIENT ______________________________________________________________________ Ouvrez le fichier uicommon.py du pack root et recherchez la class: class QuestionDialog(ui.ScriptWindow): Juste après celle class, ajoutez la class suivante: class QuestionDialogItem(ui.ScriptWindow): def __init__(self): ui.ScriptWindow.__init__(self) self.__CreateDialog() def __del__(self): ui.ScriptWindow.__del__(self) def __CreateDialog(self): pyScrLoader = ui.PythonScriptLoader() pyScrLoader.LoadScriptFile(self, "uiscript/questiondialogitem.py") self.board = self.GetChild("board") self.textLine = self.GetChild("message") self.acceptButton = self.GetChild("accept") self.destroyButton = self.GetChild("destroy") self.cancelButton = self.GetChild("cancel") def Open(self): self.SetCenterPosition() self.SetTop() self.Show() def Close(self): self.Hide() def SetWidth(self, width): height = self.GetHeight() self.SetSize(width, height) self.board.SetSize(width, height) self.SetCenterPosition() self.UpdateRect() def SAFE_SetAcceptEvent(self, event): self.acceptButton.SAFE_SetEvent(event) def SAFE_SetCancelEvent(self, event): self.cancelButton.SAFE_SetEvent(event) def SetAcceptEvent(self, event): self.acceptButton.SetEvent(event) def SetDestroyEvent(self, event): self.destroyButton.SetEvent(event) def SetCancelEvent(self, event): self.cancelButton.SetEvent(event) def SetText(self, text): self.textLine.SetText(text) def SetAcceptText(self, text): self.acceptButton.SetText(text) def SetCancelText(self, text): self.cancelButton.SetText(text) def OnPressEscapeKey(self): self.Close() return True Ouvrez maintenant le fichier game.py et cherchez la def suivante: def __DropItem(self, attachedType, attachedItemIndex, attachedItemSlotPos, attachedItemCount): Recherchez ce bout de code dans la fonction (2x): itemDropQuestionDialog = uiCommon.QuestionDialog() Remplacez par: itemDropQuestionDialog = uiCommon.QuestionDialogItem() Toujours dans la même fonction, recherchez: itemDropQuestionDialog.SetAcceptEvent(lambda arg=True: self.RequestDropItem(arg)) Ajoutez ceci juste après: itemDropQuestionDialog.SetDestroyEvent(lambda arg=True: self.RequestDestroyItem(arg)) Recherchez maintenant la fonction: def RequestDropItem(self, answer): Ajoutez cette fonction juste après: def RequestDestroyItem(self, answer): if not self.itemDropQuestionDialog: return if answer: dropType = self.itemDropQuestionDialog.dropType dropNumber = self.itemDropQuestionDialog.dropNumber if player.SLOT_TYPE_INVENTORY == dropType: if dropNumber == player.ITEM_MONEY: return else: self.__SendDestroyItemPacket(dropNumber) self.itemDropQuestionDialog.Close() self.itemDropQuestionDialog = None constInfo.SET_ITEM_QUESTION_DIALOG_STATUS(0) Recherchez ensuite la fonction: def __SendDropItemPacket(self, itemVNum, itemCount, itemInvenType = player.INVENTORY): Et ajoutez celle-ci juste après: def __SendDestroyItemPacket(self, itemVNum, itemInvenType = player.INVENTORY): if uiPrivateShopBuilder.IsBuildingPrivateShop(): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.DROP_ITEM_FAILURE_PRIVATE_SHOP) return net.SendItemDestroyPacket(itemVNum) Ouvrez maintenant le fichier locale_interface.txt et ajoutez ceci: DESTROY Destroy Placez le fichier questiondialogitem.py dans votre pack uiscript ! LIEN FE Screen du système: Voilà c'est fini ! Source du totoriel : .Avenue™ de Metin2Dev Traduction: moi Cordialement, #Saw.
  3. Verrouillage/Déverrouillage d'inventaire système de l'officiel. 1) Qu'est ce que c'est ? 2) Les Prérequis 3) Les téléchargements 1) Qu'est ce que c'est ? Le Système de Verrouillage/Déverrouillage d'inventaire est un verrou poser sur certains espaces de vos inventaire ( jusqu'à 4 inventaires ) Pour les déverrouiller il vous faut un item appeler Clé d'inventaire . Vidéos Screens Des images et vidéos valent mieux qu'un tas de mots sur une page blanche. 2) Les Prérequis Avoir des files et un client Disposez des 4 inventaires ( L'inventaires 3 et 4 doit être vide autrement cela vous empêchera de connecter le personnages en question.) Sources Client / Game / DB Les fichiers à télécharger et votre tête! 3) Les téléchargements Comme à mon habitude le tutoriel ce trouve à l'intérieur du .rar. Changelog : Sachez encore une fois que ce partage n'est pas de moi mais qu'il provient de TurkMmo. La traduction/réorganisation du tutoriel est cependant de moi. Lien mis à jour avec la correction : Cliquez ici FEV1 FEV2 GitHubV2 Cordialement, History.
  4. Salut à tous , je vais vous expliquer comment mettre 4 pages d'inventaire. Tout d'abord, allez ici : Server/common/ puis ouvrez length.h Ensuite cherchez : INVENTORY_MAX_NUM = 90, et remplacez par : INVENTORY_MAX_NUM = 180, Ensuite, dans le répertoire : game/src ouvrez char_item.cpp et cherchez : BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2); et remplacez cette ligne par : BYTE bPage = bCell / (INVENTORY_MAX_NUM / 4); Cherchez ensuite(toujours dans le char_item.cpp) : if (p / (INVENTORY_MAX_NUM / 2) != bPage) puis remplacez par : if (p / (INVENTORY_MAX_NUM / 4) != bPage) Ensuite ouvrez exchange.cpp et cherchez : static CGrid s_grid1(5, INVENTORY_MAX_NUM/5 / 2); // inven page 1 puis complétez comme ici : static CGrid s_grid1(5, INVENTORY_MAX_NUM/5 / 4); // inven page 1 static CGrid s_grid2(5, INVENTORY_MAX_NUM/5 / 4); // inven page 2 static CGrid s_grid3(5, INVENTORY_MAX_NUM/5 / 4); // inven page 3 static CGrid s_grid4(5, INVENTORY_MAX_NUM/5 / 4); // inven page 4 s_grid1.Clear(); s_grid2.Clear(); s_grid3.Clear(); s_grid4.Clear(); Ensuite, rajoutez en début de fichier ces lignes (en dessous des #include) : /* 4 INVENTORY DEFINES */ #define INVEN_PAGES 4 #define INVEN_PAGE_SIZE (INVENTORY_MAX_NUM / INVEN_PAGES) Ensuite dans la fonction , remplacez les lignes ci-dessous : for (i = 0; i < INVEN_PAGE_SIZE; ++i) { if (!(item = victim->GetInventoryItem(i))) continue; s_grid1.Put(i, 1, item->GetSize()); } for (i = INVEN_PAGE_SIZE; i < INVEN_PAGE_SIZE*2; ++i) { if (!(item = victim->GetInventoryItem(i))) continue; s_grid2.Put(i - INVEN_PAGE_SIZE, 1, item->GetSize()); } for (i = INVEN_PAGE_SIZE*2; i < INVEN_PAGE_SIZE*3; ++i) { if (!(item = victim->GetInventoryItem(i))) continue; s_grid3.Put(i - INVEN_PAGE_SIZE*2, 1, item->GetSize()); } for (i = INVEN_PAGE_SIZE*3; i < INVENTORY_MAX_NUM; ++i) { if (!(item = victim->GetInventoryItem(i))) continue; s_grid4.Put(i - INVEN_PAGE_SIZE*3, 1, item->GetSize()); } PARTIE SERVEUR TERMINÉE Passons maintenant Partie client. Pour commencer , allez ici : Client/Userinterface et ouvrez GameType.h et cherchez : const DWORD c_Inventory_Page_Count = 2; et remplacez par : const DWORD c_Inventory_Page_Count = 4; Ouvrez ensuite inventorywindow.py et cherchez : EQUIPMENT_START_INDEX = 90 que vous remplacez par : EQUIPMENT_START_INDEX = 180 Ensuite, (eh oui, c'est toujours pas terminé !) ouvrez uiinventory.py et cherchez : self.inventoryTab.append(self.GetChild("Inventory_Tab_02")) que vous ajoutez en dessous : self.inventoryTab.append(self.GetChild("Inventory_Tab_03")) self.inventoryTab.append(self.GetChild("Inventory_Tab_04")) ensuite cherchez : self.inventoryTab[1].SetEvent(lambda arg=1: self.SetInventoryPage(arg)) et ajoutez en dessous : self.inventoryTab[2].SetEvent(lambda arg=2: self.SetInventoryPage(arg)) self.inventoryTab[3].SetEvent(lambda arg=3: self.SetInventoryPage(arg)) cherchez ensuite : self.inventoryTab[0].Down() rajoutez en dessous : self.inventoryPageIndex = 0 Ensuite cherchez la fonction SetInventoryPage et remplacez par ça : def SetInventoryPage(self, page): self.inventoryTab[self.inventoryPageIndex].SetUp() self.inventoryPageIndex = page self.inventoryTab[self.inventoryPageIndex].Down() self.RefreshBagSlotWindow() PARTIE CLIENT TERMINÉE Vous pouvez tout recompiler et la partie source est terminée. Je vous partage mon inventorywindow.py qui se trouve dans locale_fr/ui : import uiScriptLocale import item EQUIPMENT_START_INDEX = 180 window = { "name" : "InventoryWindow", ## 600 - (width + i?¢´e¡Í¢¬i¨£¨öi©«¨ùe¢®©« e¢Ò¢æi?¡Æ e?i?¡Æe¢¬¡Æ 24 px) "x" : SCREEN_WIDTH - 176, "y" : SCREEN_HEIGHT - 37 - 565, "style" : ("movable", "float",), "width" : 176, "height" : 585, "children" : ( ## Inventory, Equipment Slots { "name" : "board", "type" : "board", "style" : ("attach",), "x" : 0, "y" : 0, "width" : 176, "height" : 585, "children" : ( ## Title { "name" : "TitleBar", "type" : "titlebar", "style" : ("attach",), "x" : 8, "y" : 7, "width" : 161, "color" : "yellow", "children" : ( { "name":"TitleName", "type":"text", "x":77, "y":3, "text":uiScriptLocale.INVENTORY_TITLE, "text_horizontal_align":"center" }, ), }, ## Equipment Slot { "name" : "Equipment_Base", "type" : "image", "x" : 10, "y" : 33, "image" : "d:/ymir work/ui/equipment_bg_without_ring.tga", "children" : ( { "name" : "EquipmentSlot", "type" : "slot", "x" : 3, "y" : 3, "width" : 150, "height" : 182, "slot" : ( {"index":EQUIPMENT_START_INDEX+0, "x":39, "y":37, "width":32, "height":64}, {"index":EQUIPMENT_START_INDEX+1, "x":39, "y":2, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+2, "x":39, "y":145, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+3, "x":75, "y":67, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+4, "x":3, "y":3, "width":32, "height":96}, {"index":EQUIPMENT_START_INDEX+5, "x":114, "y":67, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+6, "x":114, "y":35, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+7, "x":2, "y":145, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+8, "x":75, "y":145, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+9, "x":114, "y":2, "width":32, "height":32}, {"index":EQUIPMENT_START_INDEX+10, "x":75, "y":35, "width":32, "height":32}, ## i?? e¡Æ?i¡×¢æ1 ##{"index":item.EQUIPMENT_RING1, "x":2, "y":106, "width":32, "height":32}, ## i?? e¡Æ?i¡×¢æ2 ##{"index":item.EQUIPMENT_RING2, "x":75, "y":106, "width":32, "height":32}, ## i?? e©÷¡§i?¢¬ {"index":item.EQUIPMENT_BELT, "x":39, "y":106, "width":32, "height":32}, ), }, ## Dragon Soul Button { "name" : "DSSButton", "type" : "button", "x" : 114, "y" : 107, "tooltip_text" : uiScriptLocale.TASKBAR_DRAGON_SOUL, "default_image" : "d:/ymir work/ui/dragonsoul/dss_inventory_button_01.tga", "over_image" : "d:/ymir work/ui/dragonsoul/dss_inventory_button_02.tga", "down_image" : "d:/ymir work/ui/dragonsoul/dss_inventory_button_03.tga", }, ## MallButton { "name" : "MallButton", "type" : "button", "x" : 118, "y" : 148, "tooltip_text" : uiScriptLocale.MALL_TITLE, "default_image" : "d:/ymir work/ui/game/TaskBar/Mall_Button_01.tga", "over_image" : "d:/ymir work/ui/game/TaskBar/Mall_Button_02.tga", "down_image" : "d:/ymir work/ui/game/TaskBar/Mall_Button_03.tga", }, ## CostumeButton { "name" : "CostumeButton", "type" : "button", "x" : 78, "y" : 5, "tooltip_text" : uiScriptLocale.COSTUME_TITLE, "default_image" : "d:/ymir work/ui/game/taskbar/costume_Button_01.tga", "over_image" : "d:/ymir work/ui/game/taskbar/costume_Button_02.tga", "down_image" : "d:/ymir work/ui/game/taskbar/costume_Button_03.tga", }, { "name" : "Equipment_Tab_01", "type" : "radio_button", "x" : 86, "y" : 161, "default_image" : "d:/ymir work/ui/game/windows/tab_button_small_01.sub", "over_image" : "d:/ymir work/ui/game/windows/tab_button_small_02.sub", "down_image" : "d:/ymir work/ui/game/windows/tab_button_small_03.sub", "children" : ( { "name" : "Equipment_Tab_01_Print", "type" : "text", "x" : 0, "y" : 0, "all_align" : "center", "text" : "I", }, ), }, { "name" : "Equipment_Tab_02", "type" : "radio_button", "x" : 86 + 32, "y" : 161, "default_image" : "d:/ymir work/ui/game/windows/tab_button_small_01.sub", "over_image" : "d:/ymir work/ui/game/windows/tab_button_small_02.sub", "down_image" : "d:/ymir work/ui/game/windows/tab_button_small_03.sub", "children" : ( { "name" : "Equipment_Tab_02_Print", "type" : "text", "x" : 0, "y" : 0, "all_align" : "center", "text" : "II", }, ), }, ), }, { "name" : "Inventory_Tab_01", "type" : "radio_button", "x" : 10, "y" : 33 + 189, "default_image" : "d:/ymir work/ui/game/windows/tab_button_large_01.sub", "over_image" : "d:/ymir work/ui/game/windows/tab_button_large_02.sub", "down_image" : "d:/ymir work/ui/game/windows/tab_button_large_03.sub", "tooltip_text" : uiScriptLocale.INVENTORY_PAGE_BUTTON_TOOLTIP_1, "children" : ( { "name" : "Inventory_Tab_01_Print", "type" : "text", "x" : 0, "y" : 0, "all_align" : "center", "text" : "I", }, ), }, { "name" : "Inventory_Tab_02", "type" : "radio_button", "x" : 10 + 78, "y" : 33 + 189, "default_image" : "d:/ymir work/ui/game/windows/tab_button_large_01.sub", "over_image" : "d:/ymir work/ui/game/windows/tab_button_large_02.sub", "down_image" : "d:/ymir work/ui/game/windows/tab_button_large_03.sub", "tooltip_text" : uiScriptLocale.INVENTORY_PAGE_BUTTON_TOOLTIP_2, "children" : ( { "name" : "Inventory_Tab_02_Print", "type" : "text", "x" : 0, "y" : 0, "all_align" : "center", "text" : "II", }, ), }, { "name" : "Inventory_Tab_03", "type" : "radio_button", "x" : 10, "y" : 33 + 210, "default_image" : "d:/ymir work/ui/game/windows/tab_button_large_01.sub", "over_image" : "d:/ymir work/ui/game/windows/tab_button_large_02.sub", "down_image" : "d:/ymir work/ui/game/windows/tab_button_large_03.sub", "tooltip_text" : uiScriptLocale.INVENTORY_PAGE_BUTTON_TOOLTIP_3, "children" : ( { "name" : "Inventory_Tab_03_Print", "type" : "text", "x" : 0, "y" : 0, "all_align" : "center", "text" : "III", }, ), }, { "name" : "Inventory_Tab_04", "type" : "radio_button", "x" : 10 + 78, "y" : 33 + 210, "default_image" : "d:/ymir work/ui/game/windows/tab_button_large_01.sub", "over_image" : "d:/ymir work/ui/game/windows/tab_button_large_02.sub", "down_image" : "d:/ymir work/ui/game/windows/tab_button_large_03.sub", "tooltip_text" : uiScriptLocale.INVENTORY_PAGE_BUTTON_TOOLTIP_4, "children" : ( { "name" : "Inventory_Tab_04_Print", "type" : "text", "x" : 0, "y" : 0, "all_align" : "center", "text" : "IV", }, ), }, ## Item Slot { "name" : "ItemSlot", "type" : "grid_table", "x" : 8, "y" : 264, "start_index" : 0, "x_count" : 5, "y_count" : 9, "x_step" : 32, "y_step" : 32, "image" : "d:/ymir work/ui/public/Slot_Base.sub" }, ## Print { "name":"Money_Slot", "type":"button", "x":8, "y":28, "horizontal_align":"center", "vertical_align":"bottom", "default_image" : "d:/ymir work/ui/public/parameter_slot_05.sub", "over_image" : "d:/ymir work/ui/public/parameter_slot_05.sub", "down_image" : "d:/ymir work/ui/public/parameter_slot_05.sub", "children" : ( { "name":"Money_Icon", "type":"image", "x":-18, "y":2, "image":"d:/ymir work/ui/game/windows/money_icon.sub", }, { "name" : "Money", "type" : "text", "x" : 3, "y" : 3, "horizontal_align" : "right", "text_horizontal_align" : "right", "text" : "123456789", }, ), }, ), }, ), } Pour clore le tutoriel , allez dans locale_interface.txt et cherchez : INVENTORY_PAGE_BUTTON_TOOLTIP_2 2e inventaire et ajoutez en dessous : INVENTORY_PAGE_BUTTON_TOOLTIP_3 3e inventaire INVENTORY_PAGE_BUTTON_TOOLTIP_4 4e inventaire Vous pouvez tout recompiler, repacker et regarder le résultat. Voila, tutoriel terminé, bonne chance ! Source : M2Dev, Corrigé par Kameyu, et merci à History, Galet et Calypso pour leur participation au débug.
  5. Bonjour à tous, Aujourd'hui je vais vous expliquer rapidement comment compiler les sources enhanceMT pour la partie serveur sous Windows. Lisez bien tranquillement et tout se passera bien ! 1. Logiciels requis 2. Récupérer les sources 3. Configurer la solution avec CMake 4. Compiler ! Dans le menu, définissez le mode de compilation en "Release" et cliquez sur "Générer -> Générer la solution" (ou pressez F7). Une fois la compilation terminée vous trouverez les exécutables "db.exe" et "game.exe" dans le dossier "Server/build/bin/Release". Et voilà, c'était pas si difficile que ça au final non ? Je vous rappelle que le projet enhanceMT est un projet open source et communautaire, tout le monde peut y participer, alors n'hésitez pas à nous faire part de vos suggestions, remarques, bugs, etc. (Si possible directement sur GitHub, ici : [Hidden Content]). Sgt Fatality
  6. Bonjour à tous et à toutes, Voici mon tout premier tutoriel qui va vous permettre d'afficher l'orbe de bénédiction/cuivre magique en magasin mais aussi de supprimer la multiplication x3 du prix des items pouir la vente aux autres empires sous files 2014. --- 1. Pour commencer il faut décompiler le game, voir ici: [Hidden Content] (Merci infiniment à Calypso). 2. Nous allons aller chercher le fichier shop.cpp à cet emplacement: /usr/src/mainline/Srcs/Server/game/src et l'ouvrir 3. Pour les rendre visibles en magasin, il vous faut chercher le code ci-dessous à l'aide de "CTRL+F" : if (item->GetVnum() == 70024 || item->GetVnum() == 70035) --- 70024 est le code de l'orbe 70035 celui du cuivre --- Pour afficher l'orbe et non le cuivre vous faites comme ceci : if (item->GetVnum() == 70035) Et pour afficher le cuivre et non l'orbe vous faites comme ceci : if (item->GetVnum() == 70024) cherchez le code ci-dessous et faites de même : if (item.vnum == 70024 || item.vnum == 70035) Pour afficher les deux, vous retirez le petit bout de code ou commentez la ligne. --- 4. Le prix multiplié x3 dans un autre empire que le sien. Toujours dans le fichier shop.cpp, cherchez le code ci-dessous à l'aide de "CTRL+F" : pack2.items[i].price = item.price * 3; *3 est le chiffre par lequel on multiplie le prix des item's dans un autre empire Vous pouvez le multiplier par *1, *2, *3, *4, *5, etc... Pour ne pas multiplier le prix d'un item il vous suffit de mettre *1 Vous devez faire la même ici : if (it->second) // if other empire, price is triple dwPrice *= 3; --- 5. Vous avez fini, il ne vous reste plus qu'a compiler le game. Source : moi
  7. Bonjour ! Je vous propose un tutoriel pour installer le système de ticket partagé par Shang, que j'ai corrigé car il contenait d'assez gros problèmes côté python, permettant des injections SQL. I - Source client : Dans le fichier Packet.h : Chercher : QUEST_INPUT_STRING_MAX_NUM = 64, Ajouter juste après : #ifdef ENABLE_TICKET_SYSTEM QUEST_INPUT_STRING_LONG_MAX_NUM = 512, #endif Chercher ensuite : typedef struct command_quest_input_string { BYTE bHeader; char szString[QUEST_INPUT_STRING_MAX_NUM+1]; } TPacketCGQuestInputString; Ajouter : #ifdef ENABLE_TICKET_SYSTEM typedef struct command_quest_input_long_string { BYTE bHeader; char szString[QUEST_INPUT_STRING_LONG_MAX_NUM]; } TPacketCGQuestInputLongString; #endif Pour en finir avec ce fichier, chercher : #ifdef __AUCTION__ HEADER_CG_AUCTION_CMD = 205, #endif Puis ajouter : #ifdef ENABLE_TICKET_SYSTEM HEADER_CG_QUEST_INPUT_LONG_STRING = 214, #endif Vérifiez qu'aucun de vos packets n'utilise le 214, sinon changez en conséquence, et vous ferez de même dans les sources serveur. Dans le fichier PythonNetworkStream.h : Chercher : bool SendQuestInputStringPacket(const char * c_szString); Ajouter : #ifdef ENABLE_TICKET_SYSTEM bool SendQuestInputStringLongPacket(const char * c_pszString); #endif Dans le fichier PythonNetworkStreamModule.cpp : Chercher : PyObject* netSendQuestInputStringPacket(PyObject* poSelf, PyObject* poArgs) { char * szString; if (!PyTuple_GetString(poArgs, 0, &szString)) return Py_BuildException(); CPythonNetworkStream& rns=CPythonNetworkStream::Instance(); rns.SendQuestInputStringPacket(szString); return Py_BuildNone(); } Ajouter : #ifdef ENABLE_TICKET_SYSTEM PyObject * netSendQuestInputLongStringPacket(PyObject * poSelf, PyObject * poArgs) { char * szString; if (!PyTuple_GetString(poArgs, 0, &szString)) return Py_BuildException(); CPythonNetworkStream & rns = CPythonNetworkStream::Instance(); rns.SendQuestInputStringLongPacket(szString); return Py_BuildNone(); } #endif Chercher ensuite : { "SendQuestInputStringPacket", netSendQuestInputStringPacket, METH_VARARGS }, Ajouter : #ifdef ENABLE_TICKET_SYSTEM { "SendQuestInputLongStringPacket", netSendQuestInputLongStringPacket, METH_VARARGS }, #endif Dans le fichier PythonNetworkSteamPhaseGame.cpp : Chercher : bool CPythonNetworkStream::SendQuestInputStringPacket(const char * c_szString) { TPacketCGQuestInputString Packet; Packet.bHeader = HEADER_CG_QUEST_INPUT_STRING; strncpy(Packet.szString, c_szString, QUEST_INPUT_STRING_MAX_NUM); if (!Send(sizeof(Packet), &Packet)) { Tracen("SendQuestInputStringPacket Error"); return false; } return SendSequence(); } Ajouter : #ifdef ENABLE_TICKET_SYSTEM bool CPythonNetworkStream::SendQuestInputStringLongPacket(const char * c_pszString) { TPacketCGQuestInputLongString Packet; Packet.bHeader = HEADER_CG_QUEST_INPUT_LONG_STRING; strncpy(Packet.szString, c_pszString, QUEST_INPUT_STRING_LONG_MAX_NUM); if (!Send(sizeof(Packet), &Packet)) { Tracen("SendQuestInputStringLongPacket Error"); return false; } return SendSequence(); } #endif Dans le fichier PythonApplicationModule.cpp : Chercher : #ifdef ENABLE_COSTUME_SYSTEM PyModule_AddIntConstant(poModule, "ENABLE_COSTUME_SYSTEM", 1); #else PyModule_AddIntConstant(poModule, "ENABLE_COSTUME_SYSTEM", 0); #endif Ajouter : #ifdef ENABLE_TICKET_SYSTEM PyModule_AddIntConstant(poModule, "ENABLE_TICKET_SYSTEM", 1); #else PyModule_AddIntConstant(poModule, "ENABLE_TICKET_SYSTEM", 0); #endif Dans le fichier Locale_inc.h : Ajouter : #define ENABLE_TICKET_SYSTEM Vous pouvez maintenant lancer la compilation de vos sources client le temps qu'on s'occupe des sources serveur ! II - Source serveur : Dans le fichier service.h : Ajouter : #define ENABLE_TICKET_SYSTEM Dans le fichier input.h : Chercher : void QuestInputString(LPCHARACTER ch, const void * pvData); Ajouter : #ifdef ENABLE_TICKET_SYSTEM void QuestInputLongString(LPCHARACTER ch, const void * c_pvData); #endif Dans le fichier input_main.cpp : Chercher : void CInputMain::QuestInputString(LPCHARACTER ch, const void* c_pData) { TPacketCGQuestInputString * p = (TPacketCGQuestInputString*) c_pData; char msg[65]; strlcpy(msg, p->msg, sizeof(msg)); sys_log(0, "QUEST InputString pid %u msg %s", ch->GetPlayerID(), msg); quest::CQuestManager::Instance().Input(ch->GetPlayerID(), msg); } Ajouter : #ifdef ENABLE_TICKET_SYSTEM void CInputMain::QuestInputLongString(LPCHARACTER ch, const void * c_pvData) { const TPacketCGQuestInputLongString * p = reinterpret_cast<const TPacketCGQuestInputLongString*>(c_pvData); sys_log(0, "QUEST InputLongString pid %u msg %s", ch->GetPlayerID(), p->szMsg); quest::CQuestManager::Instance().Input(ch->GetPlayerID(), p->szMsg); } #endif Chercher : case HEADER_CG_QUEST_INPUT_STRING: QuestInputString(ch, c_pData); break; Ajouter : #ifdef ENABLE_TICKET_SYSTEM case HEADER_CG_QUEST_INPUT_LONG_STRING: QuestInputLongString(ch, c_pData); break; #endif Dans le fichier packet.h : Chercher : typedef struct command_quest_input_string { BYTE header; char msg[64+1]; } TPacketCGQuestInputString; Ajouter : #ifdef ENABLE_TICKET_SYSTEM typedef struct command_quest_input_long_string { BYTE header; char szMsg[512]; } TPacketCGQuestInputLongString; #endif Chercher : HEADER_CG_XTRAP_ACK = 204, Ajouter : #ifdef ENABLE_TICKET_SYSTEM HEADER_CG_QUEST_INPUT_LONG_STRING = 214, #endif -->Penser à modifier le packet si vous l'avez fait dans les sources client. Dans le fichier packet_info.cpp : Chercher : Set(HEADER_CG_STATE_CHECKER, sizeof(BYTE), "ServerStateCheck", false); Ajouter : #ifdef ENABLE_TICKET_SYSTEM Set(HEADER_CG_QUEST_INPUT_LONG_STRING, sizeof(TPacketCGQuestInputLongString), "QuestInputLongString", true); #endif Dans le fichier questlua_global.cpp : Si, et seulement si vous n'avez pas mysql_direct_query, vous suivez cette étape, sinon, vous pouvez dores et déjà compiler vos sources. Chercher : void RegisterGlobalFunctionTable(lua_State* L) Ajouter au dessus : #ifdef _MSC_VER #define INFINITY (DBL_MAX+DBL_MAX) #define NAN (INFINITY-INFINITY) #endif int _mysql_direct_query(lua_State* L) { // char szQuery[1024]; if (!lua_isstring(L, 1)) return 0; // strncpy(szQuery, lua_tostring(L, 1), sizeof(szQuery)); int i=0, m=1; MYSQL_ROW row; MYSQL_FIELD * field; MYSQL_RES * result; // SQLMsg * pMsg = DBManager::instance().DirectQuery(szQuery); std::auto_ptr<SQLMsg> pMsg(DBManager::instance().DirectQuery(lua_tostring(L, 1))); if (pMsg.get()) { // ret1 (number of affected rows) lua_pushnumber(L, pMsg->Get()->uiAffectedRows); //-1 if error such as duplicate occurs (-2147483648 via lua) // if wrong syntax error occurs (4294967295 via lua) // ret2 (table of affected rows) lua_newtable(L); if ((result = pMsg->Get()->pSQLResult) && !(pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1)) { // LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr(); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %u fields\n", __FUNCTION__, mysql_num_fields(result)); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %u rows\n", __FUNCTION__, mysql_num_rows(result)); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Affected %u rows\n", __FUNCTION__, pMsg->Get()->uiAffectedRows); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Num %u rows\n", __FUNCTION__, pMsg->Get()->uiNumRows); while((row = mysql_fetch_row(result))) { lua_pushnumber(L, m); lua_newtable(L); while((field = mysql_fetch_field(result))) { lua_pushstring(L, field->name); if (!(field->flags & NOT_NULL_FLAG) && (row[i]==NULL)) { // lua_pushstring(L, "NULL"); lua_pushnil(L); } else if (IS_NUM(field->type)) { double val = NAN; lua_pushnumber(L, (sscanf(row[i],"%lf",&val)==1)?val:NAN); } else if (field->type == MYSQL_TYPE_BLOB) { lua_newtable(L); for (DWORD iBlob=0; iBlob < field->max_length; iBlob++) { lua_pushnumber(L, row[i][iBlob]); lua_rawseti(L, -2, iBlob+1); } } else lua_pushstring(L, row[i]); // LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr(); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %d flag %s for %s\n", __FUNCTION__, field->type, field->name, row[i]?row[i]:"NULL"); lua_rawset(L, -3); i++; } mysql_field_seek(result, 0); i=0; lua_rawset(L, -3); m++; } } } else {lua_pushnumber(L, 0); lua_newtable(L);} // delete pMsg; return 2; } Chercher ensuite : { NULL, NULL } Ajouter au dessus : { "mysql_direct_query", _mysql_direct_query }, Vous pouvez maintenant lancer la compilation de vos sources serveur, le temps qu'on s'occupe des fichiers serveur ! III - Serveur : Vous allez dans cette partie, ajouter ceci dans votre quest_functions : ticket.answer_ticket ticket.create_ticket ticket.load_answers ticket.load_permisions ticket.load_tickets ticket.add_member ticket.update_permisions ticket.delete_member ticket.has_permision ticket.add_viewer ticket.delete_viewer Ajouter dans questlib : dofile(get_locale_base_path().."/quest/ticket_lib.so") Ajouter ensuite la lib disponible en pièce jointe, la quête et le dossiers "tickets" (Pièce jointe : QUEST.rar). IV - MySQL : je vous invite pour cette partie à lancer ces queries : SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for tickets -- ---------------------------- DROP TABLE IF EXISTS `tickets`; CREATE TABLE `tickets` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `id` varchar(8) NOT NULL, `title` varchar(60) NOT NULL, `priority` varchar(150) NOT NULL, `date` datetime NOT NULL, `status` tinyint(1) NOT NULL, `creator` varchar(16) NOT NULL, `msg` varchar(500) NOT NULL, `category` int(2) NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for tickets_answers -- ---------------------------- DROP TABLE IF EXISTS `tickets_answers`; CREATE TABLE `tickets_answers` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `id` varchar(8) NOT NULL, `creator` varchar(16) NOT NULL, `date` datetime NOT NULL, `msg` varchar(500) NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for tickets_permisions -- ---------------------------- DROP TABLE IF EXISTS `tickets_permisions`; CREATE TABLE `tickets_permisions` ( `pid` int(50) NOT NULL, `answer_permision` tinyint(1) NOT NULL, `delete_permision` tinyint(1) NOT NULL, `add_permision` tinyint(1) NOT NULL, PRIMARY KEY (`pid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; V - Client : Dans votre locale_game.txt, ajouter : TICKET_PRIORITY_HIGH Élevée TICKET_PRIORITY_MEDIUM Normale TICKET_PRIORITY_LOW Faible TICKET_STATE_ON_HOLD En cours TICKET_STATE_CLOSED Fermé TICKET_STATE_SOLVED Résolu TICKET_ADD_NAME Nom: TICKET_ADD_TITLE Ajouter un membre TICKET_ADD_ACCEPT Accepter TICKET_SEND Envoyer TICKET_TITLE Asylum : Tickets TICKET_MY_TICKETS Mes Tickets TICKET_ADMINISTRATION Administration TICKET_SORT_BY Ordre : TICKET_ID ID TICKET_TITLE_T Titre TICKET_PRIORITY Priorité TICKET_DATE Date TICKET_STATE Etat TICKET_CREATE_NEW Créer un nouveau ticket TICKET_SEARCH Rechercher par ID: TICKET_SEARCH_BUTTON Rechercher TICKET_NAME Nom TICKET_ANSWER Répondre TICKET_DELETE Supprimer TICKET_CHANGE_PERMISIONS Modifier les permissions TICKET_ADD_MEMBER Ajouter un membre TICKET_REFRESH Actualiser TICKET_CATEGORY Categorie TICKET_CATEGORY_0 Tous Dans votre uiquest.py : Chercher : def Destroy(self): self.ClearDictionary() if self.OnCloseEvent: self.OnCloseEvent() self.OnCloseEvent = None # QUEST_INPUT if self.needInputString: if self.editLine: text = self.editLine.GetText() net.SendQuestInputStringPacket(text) # END_OF_QUEST_INPUT self.imgTitle = None self.images = None self.eventCurtain = None self.board = None Remplacer par : def Destroy(self): self.ClearDictionary() if self.OnCloseEvent: self.OnCloseEvent() self.OnCloseEvent = None # QUEST_INPUT if self.needInputString: if self.editLine: text = self.editLine.GetText() if app.ENABLE_TICKET_SYSTEM: if (len(text) > 64): net.SendQuestInputLongStringPacket(text) else: net.SendQuestInputStringPacket(text) else: net.SendQuestInputStringPacket(text) # END_OF_QUEST_INPUT self.imgTitle = None self.images = None self.eventCurtain = None self.board = None Dans le fichier interfaceModule.py : Ajouter : if app.ENABLE_TICKET_SYSTEM: import uiTicket Chercher : self.dlgShop = uiShop.ShopDialog() self.dlgShop.LoadDialog() self.dlgShop.Hide() Ajouter : if app.ENABLE_TICKET_SYSTEM: self.wndTicket = uiTicket.TicketWindow() self.wndTicket.Hide() Chercher : if self.dlgShop: self.dlgShop.Destroy() Ajouter : if app.ENABLE_TICKET_SYSTEM: if self.wndTicket: self.wndTicket.Destroy() Chercher : del self.wndItemSelect Ajouter : if app.ENABLE_TICKET_SYSTEM: del self.wndTicket Dans le fichier costinfo.py : Ajouter : if app.ENABLE_TICKET_SYSTEM: Tickets = { 'QID' : 0, 'QCMD' : '', 'MY_TICKETS' : [], 'GLOBAL_TICKETS' : [], 'ANSWERS' : {}, 'PERMISIONS' : [] } CApiSetHide = 0 Dans le fichier game.py : Chercher : "mall" : self.__InGameShop_Show, Ajouter : if app.ENABLE_TICKET_SYSTEM: "TICKETS" : self.ManagerTickets, Chercher : def OpenQuestWindow(self, skin, idx): Ajouter : if app.ENABLE_TICKET_SYSTEM: if constInfo.CApiSetHide == 1: net.SendQuestInputStringPacket(str(constInfo.SendString)) constInfo.CApiSetHide = 0 return Chercher : def __InGameShop_Show(self, url): if constInfo.IN_GAME_SHOP_ENABLE: self.interface.OpenWebWindow(url) Ajouter : if app.ENABLE_TICKET_SYSTEM: def ManagerTickets(self, cmd): cmd = cmd.split('#') if cmd[0] == 'QID': constInfo.Tickets['QID'] = int(cmd[1]) elif cmd[0] == 'INPUT': constInfo.INPUT_IGNORE = int(cmd[1]) elif cmd[0] == 'SEND': net.SendQuestInputLongStringPacket(str(constInfo.Tickets['QCMD'])) constInfo.Tickets['QCMD'] = '' elif cmd[0] == 'CLEAR_CONTENT': constInfo.Tickets['MY_TICKETS'] = [] constInfo.Tickets['GLOBAL_TICKETS'] = [] elif cmd[0] == 'CLEAR_PERMISIONS': constInfo.Tickets['PERMISIONS'] = [] elif cmd[0] == 'SET_TICKET': date = cmd[4].split('[_]') constInfo.Tickets['GLOBAL_TICKETS'].append([cmd[1], cmd[2].replace('[_]', ' '), int(cmd[3]), date[0], date[1], int(cmd[5]), cmd[6], cmd[7].replace('[_]', ' '), int(cmd[8])]) if cmd[6] == player.GetName(): constInfo.Tickets['MY_TICKETS'].append([cmd[1], cmd[2].replace('[_]', ' '), int(cmd[3]), date[0], date[1], int(cmd[5]), cmd[6], cmd[7].replace('[_]', ' '), int(cmd[8])]) elif cmd[0] == 'CREATE_ANSWER': constInfo.Tickets['ANSWERS'][cmd[1]] = [] elif cmd[0] == 'SET_ANSWER': date = cmd[3].split('[_]') constInfo.Tickets['ANSWERS'][cmd[1]].append([cmd[2], date[0], date[1], cmd[4].replace('[_]', ' ')]) elif cmd[0] == 'SET_PERMISION': constInfo.Tickets['PERMISIONS'].append([cmd[1], int(cmd[2]), int(cmd[3]), int(cmd[4])]) elif cmd[0] == 'OPEN': self.interface.wndTicket.Open(int(cmd[1])) elif cmd[0] == 'REFRESH_CONTENT': self.interface.wndTicket.RefreshPage() Dans le fichier ui.py : Ajouter la class : if app.ENABLE_TICKET_SYSTEM: class CoolButton(Window): BACKGROUND_COLOR = grp.GenerateColor(0.0, 0.0, 0.0, 1.0) DARK_COLOR = grp.GenerateColor(0.4, 0.4, 0.4, 1.0) WHITE_COLOR = grp.GenerateColor(1.0, 1.0, 1.0, 0.3) HALF_WHITE_COLOR = grp.GenerateColor(1.0, 1.0, 1.0, 0.2) def __init__(self, layer = "UI"): Window.__init__(self, layer) self.eventFunc = None self.eventArgs = None self.ButtonText = None self.ToolTipText = None self.EdgeColor = None self.isOver = FALSE self.isSelected = FALSE self.width = 0 self.height = 0 def __del__(self): Window.__del__(self) self.eventFunc = None self.eventArgs = None def SetSize(self, width, height): Window.SetSize(self, width, height) self.width = width self.height = height def SetEvent(self, func, *args): self.eventFunc = func self.eventArgs = args def SetTextColor(self, color): if not self.ButtonText: return self.ButtonText.SetPackedFontColor(color) def SetEdgeColor(self, color): self.EdgeColor = color def SetText(self, text): if not self.ButtonText: textLine = TextLine() textLine.SetParent(self) textLine.SetPosition(self.GetWidth()/2, self.GetHeight()/2) textLine.SetVerticalAlignCenter() textLine.SetHorizontalAlignCenter() textLine.SetOutline() textLine.Show() self.ButtonText = textLine self.ButtonText.SetText(text) def SetToolTipText(self, text, x=0, y = -19): if not self.ToolTipText: toolTip=createToolTipWindowDict["TEXT"]() toolTip.SetParent(self) toolTip.SetSize(0, 0) toolTip.SetHorizontalAlignCenter() toolTip.SetOutline() toolTip.Hide() toolTip.SetPosition(x + self.GetWidth()/2, y) self.ToolTipText=toolTip self.ToolTipText.SetText(text) def ShowToolTip(self): if self.ToolTipText: self.ToolTipText.Show() def HideToolTip(self): if self.ToolTipText: self.ToolTipText.Hide() def SetTextPosition(self, width): self.ButtonText.SetPosition(width, self.GetHeight()/2) self.ButtonText.SetHorizontalAlignLeft() def Enable(self): wndMgr.Enable(self.hWnd) def Disable(self): wndMgr.Disable(self.hWnd) def OnMouseLeftButtonDown(self): self.isSelected = TRUE def OnMouseLeftButtonUp(self): self.isSelected = FALSE if self.eventFunc: apply(self.eventFunc, self.eventArgs) def OnUpdate(self): if self.IsIn(): self.isOver = TRUE self.ShowToolTip() else: self.isOver = FALSE self.HideToolTip() def OnRender(self): xRender, yRender = self.GetGlobalPosition() widthRender = self.width heightRender = self.height grp.SetColor(self.BACKGROUND_COLOR) grp.RenderBar(xRender, yRender, widthRender, heightRender) if self.EdgeColor: grp.SetColor(self.EdgeColor) else: grp.SetColor(self.DARK_COLOR) grp.RenderLine(xRender, yRender, widthRender, 0) grp.RenderLine(xRender, yRender, 0, heightRender) grp.RenderLine(xRender, yRender+heightRender, widthRender, 0) grp.RenderLine(xRender+widthRender, yRender, 0, heightRender) if self.isOver: grp.SetColor(self.HALF_WHITE_COLOR) grp.RenderBar(xRender + 2, yRender + 2, self.width - 3, heightRender - 3) if self.isSelected: grp.SetColor(self.WHITE_COLOR) grp.RenderBar(xRender + 2, yRender + 2, self.width - 3, heightRender - 3) Chercher (EditLine): self.eventKillFocus = None Ajouter : if app.ENABLE_TICKET_SYSTEM: self.CanClick = None Chercher : def SetTabEvent(self, event): self.eventTab = event Ajouter : if app.ENABLE_TICKET_SYSTEM: def CanEdit(self, flag): self.CanClick = flag Chercher : def OnMouseLeftButtonDown(self): En dessous de : if FALSE == self.IsIn(): return FALSE Ajouter : if app.ENABLE_TICKET_SYSTEM: if FALSE == self.CanClick: return Déplacer ensuite les fichiers fournis dans l'archive PACK.rar où il faut, et voilà ! C'est terminé. Vous pouvez maintenant ouvrir la fenêtre en utilisant la fonction : if app.ENABLE_TICKET_SYSTEM: def OpenTicketWindow(self): import event constInfo.Tickets['QCMD'] = 'OPEN#' event.QuestButtonClick(constInfo.Tickets['QID']) return Rendu (je vous renvoie sur la chaine de l'auteur) : A ploush ! TÉLÉCHARGE MOI EN CLIQUANT ICI
  8. Plop, Je vous partage l'item shop in game de shang dans sa dernière version v1.3.1 fonctionne parfaitement avec quelques petites retouches sur les files FE 2016 Dl : ICI FE Pour les problèmes et les bugs c'est dans l'A/Q/S Source : Shang
  9. Bonjour, Bonsoir Je vous présente aujourd'hui, un petit partage, qui vous permettra d'ajouter des emplacements pour vos personnages. Ceci est mon premier partage, donc toutes critiques ou autres seront une source d'améliorations. Le tutoriel se base sur l'ordre suivant : Serveur/Client/MySQL Fini de parler, passons au tutoriel : /!\ Serveur /!\ Rendez-vous dans Common/length.h Cherchez : PLAYER_PER_ACCOUNT = 4, Remplacez par : PLAYER_PER_ACCOUNT = 8, Rendez-vous maintenant dans Game/src/locale_service.cpp Cherchez : static void __CheckPlayerSlot(const std::string& service_name) { if (PLAYER_PER_ACCOUNT != 4) { printf(" PLAYER_PER_ACCOUNT = %d\n", PLAYER_PER_ACCOUNT); exit(0); } } Remplacez par : static void __CheckPlayerSlot(const std::string& service_name) { if (PLAYER_PER_ACCOUNT != 8) { printf(" PLAYER_PER_ACCOUNT = %d\n", PLAYER_PER_ACCOUNT); exit(0); } } Maintenant, allez dans Game/src/char_change_empire.cpp Cherchez : snprintf(szQuery, sizeof(szQuery), "SELECT id, pid1, pid2, pid3, pid4 FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u", get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); std::auto_ptr msg(DBManager::instance().DirectQuery(szQuery)); Remplacez par : snprintf(szQuery, sizeof(szQuery), "SELECT id, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8 FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u OR pid5=%u OR pid6=%u OR pid7=%u OR pid8=%u AND empire=%u", get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); std::auto_ptr msg(DBManager::instance().DirectQuery(szQuery)); Toujours dans char_change_empire.cpp Cherchez : MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); str_to_number(dwAID, row[0]); str_to_number(dwPID[0], row[1]); str_to_number(dwPID[1], row[2]); str_to_number(dwPID[2], row[3]); str_to_number(dwPID[3], row[4]); Remplacez par : MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); str_to_number(dwAID, row[0]); str_to_number(dwPID[0], row[1]); str_to_number(dwPID[1], row[2]); str_to_number(dwPID[2], row[3]); str_to_number(dwPID[3], row[4]); str_to_number(dwPID[4], row[5]); str_to_number(dwPID[5], row[6]); str_to_number(dwPID[6], row[7]); str_to_number(dwPID[7], row[8]); cherchez ensuite : snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u", get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); std::auto_ptr msg(DBManager::instance().DirectQuery(szQuery)); Remplacez par : snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u OR pid5=%u OR pid6=%u OR pid7=%u OR pid8=%u AND empire=%u", get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); std::auto_ptr msg(DBManager::instance().DirectQuery(szQuery)); Cherchez : DWORD CHARACTER::GetAID() const { char szQuery[1024+1]; DWORD dwAID = 0; snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u", get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); SQLMsg* pMsg = DBManager::instance().DirectQuery(szQuery); Remplacez par : DWORD CHARACTER::GetAID() const { char szQuery[1024+1]; DWORD dwAID = 0; snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u OR pid5=%u OR pid6=%u OR pid7=%u OR pid8=%u AND empire=%u", get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire()); SQLMsg* pMsg = DBManager::instance().DirectQuery(szQuery); Maintenant, rendez-vous dans db/src/clientmanager.cpp Cherchez : void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p) { char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE id=%u", GetTablePostfix(), p->bEmpire, p->dwAccountID); delete CDBManager::instance().DirectQuery(szQuery); sys_log(0, "EmpireSelect: %s", szQuery); { snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4 FROM player_index%s WHERE id=%u", GetTablePostfix(), p->dwAccountID); std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); SQLResult * pRes = pmsg->Get(); Remplacez par : void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p) { char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE id=%u", GetTablePostfix(), p->bEmpire, p->dwAccountID); delete CDBManager::instance().DirectQuery(szQuery); sys_log(0, "EmpireSelect: %s", szQuery); { snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8 FROM player_index%s WHERE id=%u", GetTablePostfix(), p->dwAccountID); std::auto_ptr pmsg(CDBManager::instance().DirectQuery(szQuery)); SQLResult * pRes = pmsg->Get(); Cherchez : void CClientManager::ChangeMonarchLord(CPeer * peer, DWORD dwHandle, TPacketChangeMonarchLord* info) { char szQuery[1024]; snprintf(szQuery, sizeof(szQuery), "SELECT a.name, NOW() FROM player%s AS a, player_index%s AS b WHERE (a.account_id=b.id AND a.id=%u AND b.empire=%u) AND " "(b.pid1=%u OR b.pid2=%u OR b.pid3=%u OR b.pid4=%u)", GetTablePostfix(), GetTablePostfix(), info->dwPID, info->bEmpire, info->dwPID, info->dwPID, info->dwPID, info->dwPID); SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); Remplacez par : void CClientManager::ChangeMonarchLord(CPeer * peer, DWORD dwHandle, TPacketChangeMonarchLord* info) { char szQuery[1024]; snprintf(szQuery, sizeof(szQuery), "SELECT a.name, NOW() FROM player%s AS a, player_index%s AS b WHERE (a.account_id=b.id AND a.id=%u AND b.empire=%u) AND " "(b.pid1=%u OR b.pid2=%u OR b.pid3=%u OR b.pid4=%u OR b.pid5=%u OR b.pid6=%u OR b.pid7=%u OR b.pid8=%u)", GetTablePostfix(), GetTablePostfix(), info->dwPID, info->bEmpire, info->dwPID, info->dwPID, info->dwPID, info->dwPID, info->dwPID, info->dwPID, info->dwPID, info->dwPID); SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); Rendez-vous ensuite dans db/src/clientmanagerlogin.cpp Cherchez : void CClientManager::QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p) Ensuite, trouvez les lignes : sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip); char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id); CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); Remplacez par : sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip); char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id); CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); Cherchez ensuite : void CClientManager::RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg) Trouvez les lignes : if (msg->Get()->uiNumRows == 0) { DWORD account_id = info->pAccountTable->id; char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), account_id); std::auto_ptr pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER)); Et remplacez par : if (msg->Get()->uiNumRows == 0) { DWORD account_id = info->pAccountTable->id; char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), account_id); std::auto_ptr pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER)); Maintenant, cherchez : void CClientManager::RESULT_PLAYER_INDEX_CREATE(CPeer * pkPeer, SQLMsg * msg) Trouvez les lignes : char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), info->pAccountTable->id); CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); Remplacez par : char szQuery[QUERY_MAX_LEN]; snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), info->pAccountTable->id); CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); /!\ Client /!\ Rendez-vous dans Client/UserInterface/Packet.h Cherchez : PLAYER_PER_ACCOUNT3 = 3, PLAYER_PER_ACCOUNT4 = 4, Remplacez par : PLAYER_PER_ACCOUNT3 = 7, PLAYER_PER_ACCOUNT4 = 8, Rendez-vous maintenant dans Pack/root/introselect.py Cherchez : SLOT_COUNT = 4 Remplacez par : SLOT_COUNT = 8 Cherchez : SLOT_ROTATION = [135.0, 225.0, 315.0, 45.0] Remplacez par : SLOT_ROTATION = [135.0, 180.0, 225.0, 270.0, 315.0, 360.0, 45.0, 90.0] Cherchez : chr.DeleteInstance(0) chr.DeleteInstance(1) chr.DeleteInstance(2) chr.DeleteInstance(3) Remplacez par : chr.DeleteInstance(0) chr.DeleteInstance(1) chr.DeleteInstance(2) chr.DeleteInstance(3) chr.DeleteInstance(4) chr.DeleteInstance(5) chr.DeleteInstance(6) chr.DeleteInstance(7) Cherchez : indexArray = (3, 2, 1, 0) Remplacez par : indexArray = (8, 7, 6, 5, 4, 3, 2, 1, 0) Cherchez : def OnKeyDown(self, key): if 1 == key: self.ExitSelect() if 2 == key: self.SelectSlot(0) if 3 == key: self.SelectSlot(1) if 4 == key: self.SelectSlot(2) if 5 == key: self.SelectSlot(3) Remplacez par : def OnKeyDown(self, key): if 1 == key: self.ExitSelect() if 2 == key: self.SelectSlot(0) if 3 == key: self.SelectSlot(1) if 4 == key: self.SelectSlot(2) if 5 == key: self.SelectSlot(3) if 6 == key: self.SelectSlot(4) if 7 == key: self.SelectSlot(5) if 8 == key: self.SelectSlot(6) if 9 == key: self.SelectSlot(7) /!\ MySQL /!\ Rendez-vous dans Player/Player_index Modifiez : `id` int(11) NOT NULL DEFAULT '0', `pid1` int(11) NOT NULL DEFAULT '0', `pid2` int(11) NOT NULL DEFAULT '0', `pid3` int(11) NOT NULL DEFAULT '0', `pid4` int(11) NOT NULL DEFAULT '0', `empire` tinyint(4) NOT NULL DEFAULT '0', Par : `id` int(11) NOT NULL DEFAULT '0', `pid1` int(11) NOT NULL DEFAULT '0', `pid2` int(11) NOT NULL DEFAULT '0', `pid3` int(11) NOT NULL DEFAULT '0', `pid4` int(11) NOT NULL DEFAULT '0', `pid5` int(11) NOT NULL DEFAULT '0', `pid6` int(11) NOT NULL DEFAULT '0', `pid7` int(11) NOT NULL DEFAULT '0', `pid8` int(11) NOT NULL DEFAULT '0', `empire` tinyint(4) NOT NULL DEFAULT '0', Voilà, vous venez d'ajouter des emplacements supplémentaire sur vos comptes. Aperçu ? Le screen du système comporte les 8 emplacements. Je vous conseil de mettre 6 emplacements pour éviter une "surcharge" des personnages. Bien entendu, si quelqu'un souhaite modifier/corriger les codes, je suis d'accord car je ne suis pas trop calé niveau sources. Source : Turkmmo
  10. Bonjour, Ce tutoriel-partage va vous aidez à fix le bug de pouvoir switch les bonus d'un équipement déjà équiper et du coup fix l'accumulation des bonus. Rendez vous dans dans le fichier char_item.cpp Cherchez : if (ITEM_COSTUME == item2->GetType()) { ChatPacket(CHAT_TYPE_INFO, LC_TEXT("ĽÓĽşŔ» şŻ°ćÇŇ Ľö ľř´Â ľĆŔĚĹŰŔÔ´Ď´Ů.")); return false; } if (item2->IsExchanging()) return false; Ajoutez en dessous : if (item2->IsEquipped()) { ChatPacket(CHAT_TYPE_INFO, LC_TEXT("no.switch")); return false; } /!\ Attention à bien mettre les tabulations /!\ Dans le même fichier cherchez : case 71052 : // ÁøÀç°æ { // À¯·´, ½Ì°¡Æú, º£Æ®³² ÁøÀç°¡ »ç¿ë±ÝÁö //if (LC_IsEurope() || LC_IsSingapore() || LC_IsVietnam()) //return false; LPITEM item2; if (!IsValidItemPosition(DestCell) || !(item2 = GetItem(DestCell))) return false; if (item2->IsExchanging() == true) return false; if (item2->GetAttributeSetIndex() == -1) { ChatPacket(CHAT_TYPE_INFO, LC_TEXT("¼Ó¼ºÀ» º¯°æÇÒ ¼ö ¾ø´Â ¾ÆÀÌÅÛÀÔ´Ï´Ù.")); return false; } if (item2->ChangeRareAttribute() == true) { char buf[21]; snprintf(buf, sizeof(buf), "%u", item2->GetID()); LogManager::instance().ItemLog(this, item, "CHANGE_RARE_ATTR", buf); item->SetCount(item->GetCount() - 1); } else { ChatPacket(CHAT_TYPE_INFO, LC_TEXT("º¯°æ ½Ãų ¼Ó¼ºÀÌ ¾ø½À´Ï´Ù")); } } Entre "if (item2->GetAttributeSetIndex() == -1)" et "if (item2->ChangeRareAttribute() == true)" Ajoutez ceci : if (item2->IsEquipped()) { ChatPacket(CHAT_TYPE_INFO, LC_TEXT("no.switch")); return false; } /!\ Attention à bien mettre les tabulations /!\ Partie source c'est fini ! Ensuite rendez vous ici => /usr/metin2/share/locale/france/locale_string.txt/ Et ajoutez à la fin du fichier : "no.switch"; "Vous ne pouvez pas switch les bonus d'un équipement équiper"; /!\ Attention à bien laisser une ligne vide à la fin du fichier /!\ Source : Just4Metin et moi
  11. Niveau requis : Intermédiaire Temps estimé : 15 minutes Introduction Dans ce tutoriel, je vais vous apprendre à changer la version de Python sur votre client Metin2, Python 2.2 à Python 2.7. Pour cela vous aurez besoin de vos sources clients et de pouvoir les compiler. Cette amélioration permet d'augmenter légèrement la vitesse du client pour peu de modifications internes. Pré-requis Savoir compiler le client binary... Eternexus pour depack le root... Vos sources client, sinon les Sources de Metin2... Téléchargement : ICI FE... I. Migration II. Adaptation Succès ! Votre client est maintenant prêt pour Python 2.7 ! Pour continuer Si vous avez un problème n'hésitez pas à poster votre demande d'aide dans l'AQS Source : MT2Dev / Kijaru
  12. Niveau requis : Intermédiaire Temps estimé : Entre 5 et 10 minutes Réécriture by Xayah Bonjour à tous, En me baladant sur un forum j'ai trouvé un mini tutoriel qui n'a pas encore été partagé ici à ma connaissance. L'objectif est de modifier le temps de ramassages entre 2 items au sol. Pré-requis : Vos sources client I. Tutoriel Source : MT2Dev Pour continuer : Si vous ne savez pas comment compiler votre lanceur, je vous invite à lire ce tutoriel : Compiler le client Binary Si vous avez un problème n'hésitez pas à poster votre demande dans l'A/Q/S
  13. Bonsoir ! Après une semaine à essayer de compiler le client de metin2, je vous fait un petit tutoriel. Tout d'abord, il vous faut les sources de metin : ICI Pour compiler le client, je vous conseille la branche novaline mais vous pouvez très bien utiliser la branche mainline. Ensuite, vous conseille Visual Studio 2013, car le 2008 est foireux et vous risquez d'avoir des erreurs dans votre compilation (je parle en connaissance de cause ) Vous pouvez le télécharger ici: [Hidden Content] Etape 1, préparez son environnement Une fois les sources et le logiciel téléchargé, ouvrez le. Cliquez sur l'icône ouvrir un fichier et choisissez ce sln: Metin2Client_VC90.sln Il se trouve dans Srcs\Client ----------------------------------------------------------------------- Maintenant, créez un dossier lib et include où vous voulez. Vous pouvez le faire à la racine de votre disque dur, sur le bureau, peu importe. On va dans chaque dossier, mettre les fichiers nécessaire au code source. DOSSIER LIB Srcs\Tools\WorldEditor\extern\lib Mettez le contenu de ce dossier, dans le dossier lib que vous avez créé. Srcs\Extern\lib Mettez le contenu de ce dossier, dans le dossier lib que vous avez créé. DOSSIER INCLUDE Srcs\Tools\WorldEditor\extern\include Mettez le contenu de ce dossier, dans le dossier include que vous avez créé. Srcs\Extern\include Mettez le contenu de ce dossier, dans le dossier include que vous avez créé. !/ N'hésitez surtout pas à remplacer /! ------------------------------------------------------------------------ Avant de lier vos dossier à votre code, je vous conseille de changer le mode de compilation. Personnellement, j'utilise le mod Release qui me permet d'avoir un lanceur d’environ 3200Ko Pour changer le mod de compilation, faites un clic droit sur votre solution (votre .sln) dans visual studio puis cliquez sur propriété. Allez dans Propriétés de configuration ---> Configuration. Cliquez sur Gestionnaire de Configuration et dans configuration de la solution active, vous choisissez Release. Vos projets vont se mettre en Release, il vous reste juste à fermer la fenêtre et cliquer sur OK. ------------------------------------------------------------------------ Maintenant, il faut lier chaque projet de votre solution à vos dossiers. Faites un clic droit sur UserInterface puis cliquez sur Propriété. Allez dans Configuration ---> Vc++ et dans répertoire include, vous choisissez votre dossier include, comme sur le screen. Dans Répertoires de bibliothèques, vous choisissez votre dossier lib. Vous faites exactement la même chose pour les 15 autres projets (EterPack, EterLib, etc ...) ------------------------------------------------------------------------ Maintenant il vous suffit juste d'appuyer sur ces touches pour compiler (en même temps): CTRL + SHIFT + B Pour recompiler le client, faites un clic droit sur la solution puis: Régénérer la solution Malheureusement, la compilation est assez longue. Le .exe final se trouvera dans le dossier /UserInterface/Release Les erreurs fréquentes Impossible d'ouvrir mon sln Votre sln n'utilise juste pas le bonne version. Vous pouvez résoudre ce problème tout simplement. Éditez avec NotePad votre SLN et à la place de: Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 Mettez(si vous utilisez Visual 2008): Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2008 Ca varie en fonction de la version de votre IDE, pensez à bien le retenir 16>.AffectFlagContainer.cpp(68 ) : error C2440: 'initializing' : cannot convert from 'CAffectFlagContainer::Element' to 'char &' 16>.\AffectFlagContainer.cpp(68 ) : error C2440: 'initializing' : cannot convert from 'CAffectFlagContainer::Element' to 'char &' Cliquez deux fois sur CAffectFlagContainer et cela va vous amener à un BYTE. Faites clic droit sur BYTE et cliquez sur Atteindre la définition Il suffit juste de remplacer char par unsigned char. 6>.PythonGraphic.cpp(294) : error C2664: 'CPythonGraphic::SaveJPEG' : cannot convert parameter 2 from 'char *' to 'LPBYTE' 6>.\PythonGraphic.cpp(294) : error C2664: 'CPythonGraphic::SaveJPEG' : cannot convert parameter 2 from 'char *' to 'LPBYTE' Double cliquez sur l'erreur et ça va vous amener sur la ligne de l'erreur. Effacez cette ligne et mettez: bool bSaved = SaveJPEG(c_pszFileName, (LPBYTE)pbyBuffer, uWidth, uHeight); Erreur Python Il arrive parfois, si vous utilisez Python-2.2, vous ayez une erreur. La même chose pour Python2.7. Trouvez donc dans un premier temps, l'erreur qui concerne cette ligne: PyAPI_FUNC(struct symtable *) PySymtable_Build(mod_ty, const char *, PyFutureFeatures *); Et commentez la. (Pour commenter: //commentaire) module' object has no attribute 'EQUIPMENT_RING1 Erreur Sysser 'module' object has no attribute 'EQUIPMENT_RING1' Dans le fichier: Locale_inc.h. Vous supprimez tout et vous mettez: #define LOCALE_SERVICE_SINGAPORE #define ENABLE_COSTUME_SYSTEM #define ENABLE_ENERGY_SYSTEM #define ENABLE_DRAGON_SOUL_SYSTEM #define ENABLE_NEW_EQUIPMENT_SYSTEM 16>cryptlib-5.6.1MTd.lib(iterhash.obj) : error LNK2001: unresolved external symbol \"void __cdecl std::_Xbad_alloc(void)" ([email protected]@@YAXXZ) 16>cryptlib-5.6.1MTd.lib(iterhash.obj) : error LNK2001: unresolved external symbol "void __cdecl std::_Xbad_alloc(void)" ([email protected]@@YAXXZ) Si vous avez des erreurs avec la lib Cryptlib-5.6.1MT, il faut recompiler cette lib. Pour se faire, avec Visual Studio, ouvrez le fichier cryptlib.2008.sln qui se trouve dans le dossier: Srcs\Extern\cryptopp Vous importez la solution, vous compilez avec CTRL + SHIFT + B et une fois terminé, la lib se trouvera dans Srcs\Extern\lib. Sous le nom de: cryptlib-5.6.1MTd.lib Attention ! Il faut compiler la lib en MT et sur le même toolset que le client ! Dans tous les cas, vous pouvez très simplement trouver cette lib avec la date du fichier. 5>mss32.lib(mss32.dll) : error LNK2026: module unsafe pour l'image SAFESEH. 5>mss32.lib(mss32.dll) : error LNK2026: module unsafe pour l'image SAFESEH. Il suffit de désactiver / SAFESEH. Pour cela, clique droit sur UserInterface, propriétés puis cliquez sur éditeur de liens, allez dans avancé puis sur la ligne Image avec gestionnaire d'exceptions sécurisés, vous mettez non. Vous régénérez la solution, et le tour est joué TraceBack - Error: Bad magic number in lib libtraceback.pyc L'erreur dans la fenêtre LOG: TraceBack - Error: Bad magic number in lib lib\traceback.pyc Le soucis vient du fait que votre lanceur est dans une certaine version de python alors que vos libs (Dans client, le dossier lib) utilisent une autre version de Python. En fait, on peut appeler le magic number, une clé pong mais avec un contexte bien différent. Vos libs, quand vous les éditez en HEXA, contiennent le nombre magique correspondant à votre python.dll python27.dll a son propre magic number tout comme python22. C'est une sorte de laison. Pour résoudre ce problème, vous avez deux choix: Passez vos libs client à la version python de votre lanceur ou alors compiler votre lanceur avec la version Python correspondant à votre client. TraceBack Hé oui, vous pouvez aussi avoir une fenêtre d'erreur mais juste avec l'erreur TraceBack. Cela signifie qu'un de votre dossier pack n'est pas apte à prendre en compte ce lanceur compilé (je simplifie, sinon j'écris un topic sur cette erreur). En gros c'est que vos fichiers n'utilisent pas les bonnes fonctions. Dans ce cas, je vous conseille de télécharger ce client: ICI Vous essayez de prendre les fichiers root et voir si votre client se lance sans cette erreur. (vous faites vos modif après) Si root ne fonctionne pas, essayé locale_* ou alors essayez uiscript. Il y en a bien un qui va résoudre votre soucis. La plus part du temps, c'est root. LoginWindow.__LoadScript.LoadObject - :Python int too large to convert to C long À l'entête du fichier: PythonUtils.cpp, ajoutez: #define PyLong_AsLong PyLong_AsLongLong #define PyLong_AsUnsignedLong PyLong_AsUnsignedLongLong Vous pouvez apprendre à passer de Python 2.2 à Python 2.7 grâce à ce tutoriel : ICI Source: Moi et en m'aidant de m2d pour la correction de certaines erreurs
  14. Niveau requis : Intermédiaire Temps estimé : Entre 15 et 30 minutes Réécriture by Xayah Bonjour, Ce système ressemble à celui avec l'affichage des Metins sur la Minimap, cette fois ce sont les Boss qui sont concernés. Pré-requis: Des sources client Votre client Notepadd++ I. Partie Sources Client II. Partie Python Pour continuer: Si vous avez un problème n'hésitez pas à poster vos demandes dans la section AQS !
  15. Niveau requis : Intermédiaire Temps estimé : Entre 10 et 15 minutes Réécriture by Xayah Bonjour, bonsoir, Aujourd'hui, je vais vous présenter une fonction qui peut avoir plus ou moins d’intérêt pour tout un chacun ! En effet, cette fonction permet au jeu de savoir quand est-ce que la fenêtre d'entrepôt s'ouvre ou se ferme. À première vue, il y a peu d'utilité, mais dès lors que vous savez quoi en faire, cette fonction peut sévère immensément utile ! (Voir vidéo ci-bas) Pré-requis: Citer les tutoriels/connaissances/logiciels requis pour réaliser ce tuto I. Quelques précisions II. Partie Sources Client III. Partie Client (Python) A savoir : Si vous ne savez pas comment compiler votre lanceur veuillez suivre le tutoriel suivant : Compiler le client Binary Si vous ne savez pas comment depack votre client, suivez ce tutoriel : Depack/Repack avec Eternexus Si vous avez un problème n'hésitez pas à poster votre demande dans la section AQS
  16. Skill Sage Grand Maître. 1) Qu'est ce que cela? 2) Les Prérequis. 3) Le téléchargement. 1) Qu'est ce que cela? Alors, les skill Sage Grand Maître sont des skills au-dessus de P ( Oui vous l'avez compris, vous pourrez up vos skills plus de P) Les skills s'upent de la même manière que pour up en P (de G1 à P ) Ils s'up de P1 à S Pour up vos skills vous aurez besoin de lire des Sage Pierre d'âme (Oh non pas encore des PA !!!!!!!!!!!!!!!!) Vidéos Une vidéo est toujours explicite que quelques mots sur une page blanche. 2) Les Prérequis. Des Files et un client Sources Client/Game/Db De l'archive en téléchargement et votre tête. Sachez encore une fois que ce partage n'est pas de moi et provient de Board Legend, ce système à été créé par Lennt. La traduction est cependant de moi. (Si quelqu'un pourrais traduire le skilldesc.txt je suis preneur.) 3) Le téléchargement. Cliquez ici FE Cordialement, History.
  17. Niveau requis : Intermédiaire Temps estimé : Entre 15 & 30 minutes Bonjour à tous, Réécriture by Xayah Vous voulez compiler votre propre dump_proto ? Par ici alors ! Mais avant toute chose je vais vous expliquer qu'est-ce qu'un dumpproto. Alors le DumpProto est un exécutable (un petit mini logiciel) qui servira à créer vos item_proto et mob_proto côté client ! Ah oui et il est seulement utile que pour les files 2013 + (files fonctionnant sous .txt). Information. Il faut savoir qu'un dumpproto est propre à un serveur, pourquoi ? Car si vous ajoutez des systèmes tels que les étoles ou l'anti-magie ou encore la liaison d'âme vous allez devoir modifier les colonnes de vos item_proto et/ou mob_proto et de ce fait vous allez devoir aussi modifier la lecture de votre dump_proto donc on ne peut pas piquer le dump du serveur voisin si vous avez pas les mêmes fichiers que lui . Pré-requis: Visual Studio 2013 Des sources dump voici le lien : HERE FE I. Compiler son DumpProto II. Utiliser son DumpProto Pour continuer: Si vous souhaitez en savoir plus sur les flags & compagnie je vous renvoie vers ce tutoriel : Configurer ses items Si vous avez un problème n'hésitez pas à poster un sujet dans la section AQS !
  18. Bonsoir à tous, Aujourd'hui, je vous partage l'instance Meley et le Temple d'Ochao version officielle. Lien de téléchargement pour l'instance Meley : ICI Lien de téléchargement pour le Temple d'Ochao : ICI Lien FE : ICI Source : TurkMMO Cordialement
  19. Salut à tous, J'ai eu plusieurs requête par rapport à mes systèmes dont celui qui permet de cacher n'importe quel gr2 à cause des lags. J'ai appris que celui-ci avait fuité, donc j'ai décidé de faire une nouvelle version. C'est la raison pour la qu'elle je viens vous partager celle-ci. Pour ceux qui me l'ont acheté directement, veuillez me contacter en MP, je vous ferai une v2. Elle inclura le hide des étoles & costumes. Quelques informations avant tout : AFFECT_INVISIBILITY est bugé de base (essayez de vous inv avec un logo GM, une fois réapparu vous ne le verrez plus) J'avais trouvé un fix par hasard sur metin2dev il y a quelques mois, je vous le partagerais dans les commentaires si je le retrouve. Ce système est dynamique et le code est léger. Il possède également des define côté C++ mais j'ai pas pensé à le faire côté python. Merci de ne pas vendre ce système. Le tutoriel se trouve dans le .rar Une petite vidéo concernant le fonctionnement : Lien de téléchargement : Cliquez-ici ! FE
  20. Salut à tous, Je viens vous partager les résultats de mes test après quelques mois. Moins de déconnexion inexpliqué lié au game. Plus aucune erreur lié au séquence dans les syserrs Sources serveur & client allégés Comme vous pouvez le constater, la suppression de ce système est entièrement bénéfique puisque c'est un système incomplet provoquant des déconnexion en jeu sans aucune raison. Le système générait également cette erreur côté serveur: Par contre, si vous avez désactiver l'encryption des packets sur vos sources, ça nécessitera beaucoup d'autres modifications assez importantes. Si vous voulez faire une série de test vous avez la possibilité d'agir de 3 façons différentes : #ifndef SEQUENCE_SYSTEM_DISABLED avant chaque code lié aux séquences. Vous commentez le code Vous supprimez le code pour alléger votre sources (j'ai opté pour celle-ci) Commençons, Ouvrez input.cpp et cherchez : if (bHeader == HEADER_CG_PONG) sys_log(0, "PONG! %u %u", m_pPacketInfo->IsSequence(bHeader), *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE))); Ici, le syslog lié au Header de la clé pong fait appel à la fonction IsSequence, on va modifier ça par : if (bHeader == HEADER_CG_PONG) sys_log(0, "PONG! %u", *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE))); Juste en bas vous avez : if (m_pPacketInfo->IsSequence(bHeader)) { BYTE bSeq = lpDesc->GetSequence(); BYTE bSeqReceived = *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE)); if (bSeq != bSeqReceived) { sys_err("SEQUENCE %x mismatch 0x%x != 0x%x header %u", get_pointer(lpDesc), bSeq, bSeqReceived, bHeader); LPCHARACTER ch = lpDesc->GetCharacter(); char buf[1024]; int offset, len; offset = snprintf(buf, sizeof(buf), "SEQUENCE_LOG [%s]-------------\n", ch ? ch->GetName() : "UNKNOWN"); if (offset < 0 || offset >= (int) sizeof(buf)) offset = sizeof(buf) - 1; for (size_t i = 0; i < lpDesc->m_seq_vector.size(); ++i) { len = snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", lpDesc->m_seq_vector[i].hdr, lpDesc->m_seq_vector[i].seq); if (len < 0 || len >= (int) sizeof(buf) - offset) offset += (sizeof(buf) - offset) - 1; else offset += len; } snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", bHeader, bSeq); sys_err("%s", buf); lpDesc->SetPhase(PHASE_CLOSE); return true; } else { lpDesc->push_seq(bHeader, bSeq); lpDesc->SetNextSequence(); //sys_err("SEQUENCE %x match %u next %u header %u", lpDesc, bSeq, lpDesc->GetSequence(), bHeader); } } Ajoutez le ifdef, commentez ou supprimez l'intégralité du code. Cherchez : CInputHandshake::CInputHandshake() { CPacketInfoCG * pkPacketInfo = M2_NEW CPacketInfoCG; pkPacketInfo->SetSequence(HEADER_CG_PONG, false); m_pMainPacketInfo = m_pPacketInfo; BindPacketInfo(pkPacketInfo); } On va supprimer l’envoi de la séquence de la clé pong ici comme ceci : CInputHandshake::CInputHandshake() { CPacketInfoCG * pkPacketInfo = M2_NEW CPacketInfoCG; // pkPacketInfo->SetSequence(HEADER_CG_PONG, false); m_pMainPacketInfo = m_pPacketInfo; BindPacketInfo(pkPacketInfo); } C'est tout pour l'input.cpp, ouvrons le fichier desc.cpp : #include "sequence.h" Ajoutez un commentaire à l'include. Ensuite cherchez et commentez le code : m_iCurrentSequence Cherchez et commentez le code: m_seq_vector.clear(); Cherchez à nouveau & commentez le code : m_seq_vector.clear(); Cherchez : BYTE DESC::GetSequence() { return gc_abSequence[m_iCurrentSequence]; } void DESC::SetNextSequence() { if (++m_iCurrentSequence == SEQUENCE_MAX_NUM) m_iCurrentSequence = 0; } Commentez l'intégralité. Pour finir, cherchez : void DESC::push_seq(BYTE hdr, BYTE seq) { if (m_seq_vector.size()>=20) { m_seq_vector.erase(m_seq_vector.begin()); } seq_t info = { hdr, seq }; m_seq_vector.push_back(info); } Commentez également l'intégralité de la fonction. Ouvrons le desc.h pour les déclarations. Cherchez : // sequence 버그 찾기용 데이타 struct seq_t { BYTE hdr; BYTE seq; }; typedef std::vector<seq_t> seq_vector_t; // sequence 버그 찾기용 데이타 Commentez le tout. Cherchez : BYTE GetSequence(); void SetNextSequence(); Commentez les 2 fonctions. Cherchez : int m_iCurrentSequence; Commentez la fonction. Cherchez : public: seq_vector_t m_seq_vector; void push_seq (BYTE hdr, BYTE seq); Commentez tout. Ouvrons le packet_info.cpp et cherchons : void CPacketInfo::Set(int header, int iSize, const char * c_pszName, bool bSeq) { if (m_pPacketMap.find(header) != m_pPacketMap.end()) return; TPacketElement * element = M2_NEW TPacketElement; element->iSize = iSize; element->stName.assign(c_pszName); element->iCalled = 0; element->dwLoad = 0; element->bSequencePacket = bSeq; if (element->bSequencePacket) element->iSize += sizeof(BYTE); m_pPacketMap.insert(std::map<int, TPacketElement *>::value_type(header, element)); } Modifiez par: void CPacketInfo::Set(int header, int iSize, const char * c_pszName) { if (m_pPacketMap.find(header) != m_pPacketMap.end()) return; TPacketElement * element = M2_NEW TPacketElement; element->iSize = iSize; element->stName.assign(c_pszName); element->iCalled = 0; element->dwLoad = 0; /* element->bSequencePacket = bSeq; if (element->bSequencePacket) element->iSize += sizeof(BYTE); */ m_pPacketMap.insert(std::map<int, TPacketElement *>::value_type(header, element)); } Cherchez : bool CPacketInfo::IsSequence(int header) { TPacketElement * pkElement = GetElement(header); return pkElement ? pkElement->bSequencePacket : false; } void CPacketInfo::SetSequence(int header, bool bSeq) { TPacketElement * pkElem = GetElement(header); if (pkElem) { if (bSeq) { if (!pkElem->bSequencePacket) pkElem->iSize++; } else { if (pkElem->bSequencePacket) pkElem->iSize--; } pkElem->bSequencePacket = bSeq; } } Commentez la totalité. Maintenant, on va modifier tout les packets de la fonction CPacketInfoCG::CPacketInfoCG(). L’envoi de séquence n'étant plus nécessaire, remplacez ceci : Set(HEADER_CG_GUILD_SYMBOL_UPLOAD, sizeof(TPacketCGGuildSymbolUpload), "SymbolUpload", false); Par : Set(HEADER_CG_GUILD_SYMBOL_UPLOAD, sizeof(TPacketCGGuildSymbolUpload), "SymbolUpload"); Faites ceci pour tous les header avec un true ou un false. Vous devriez avoir quelque comme comme ça: Ouvrons le packet_info.h et cherchons : typedef struct SPacketElement { int iSize; std::string stName; int iCalled; DWORD dwLoad; bool bSequencePacket; } TPacketElement; Modifiez par: typedef struct SPacketElement { int iSize; std::string stName; int iCalled; DWORD dwLoad; //bool bSequencePacket; } TPacketElement; Cherchez : void Set(int header, int size, const char * c_pszName, bool bSeq=false); Modifiez par : void Set(int header, int size, const char * c_pszName); Cherchez et commentez le code : bool IsSequence(int header); void SetSequence(int header, bool bSeq); Pour conclure le tutoriel, ouvrons input_udp.cpp et cherchons : Set(1, sizeof(ServerStateChecker_RequestPacket), "ServerStateRequest", false); Modifiez par : Set(1, sizeof(ServerStateChecker_RequestPacket), "ServerStateRequest"); Enfin, supprimez les 2 fichiers séquence de vos sources et n'oubliez pas de les retirer du makefile. Pour la partie client je ne vais pas faire de tutoriel étant donné que ce fichier n'est jamais modifié donc vous pouvez prendre le miens (j'en ai refait un pour vous vu que j'ai désactivé l’encryption des packets de mon côté). Lien de téléchargement : Cliquez-ici ! FE Source : Madara, merci de citer la source si vous souhaitez partager ce tutoriel ailleurs.
  21. Bonjour ! Je vien aujourd'hui vous partager un système d'effet qui vous permettra d'ajouter des effets à n'importe quel armures ou costumes par un fichier .py Téléchargement : ICIV2 - FEV1 - FEV2 Source : Freakgamer Edit : Ajout du code pour les effets sur les costumes dans le fichier InstanceBase.cpp Si vous avez des soucis avec ce système, je vous demande de poster votre problèmes dans la section Aide / Questions / Support.
  22. Bonjour/Bonsoir ! Dans ce tutoriel, je vais vous expliquer comment passer de Granny 2.4 à Granny 2.9. Ce tutoriel se passera dans les sources client. Donc, pour commencer, cherchez "granny_common_2_9_12_0_release.zip" dans le fichier extern de vos sources Novaline (si vous ne l'avez pas, le lien de téléchargement de l'archive serra mis à la fin du tutoriel). Une fois ce fichier trouvé, il vous faudra l'extraire puis prendre granny.h dans le dossier include puis le remplacer dans vos sources client (dans le fichier include). Prennez les fichiers .lib et .pdb dans le dossier lib/win32 puis mettez le dans vos sources client (fichier lib). Une fois ceci fais, attaquons le code ! (Attention, il faut faire les tabulation sois même !) Rendez vous dans Mesh.cpp du projet EterGrnLib. Cherchez: int * boneIndices = GrannyGetMeshBindingToBoneIndices(pgrnMeshBinding); Remplacez par: int * boneIndices = (int*)GrannyGetMeshBindingToBoneIndices(pgrnMeshBinding); Cherchez: return GrannyGetMeshBindingToBoneIndices(m_pgrnMeshBindingTemp); Remplacez par: return (int*)GrannyGetMeshBindingToBoneIndices(m_pgrnMeshBindingTemp); Cherchez: m_pgrnMeshDeformer = GrannyNewMeshDeformer(pgrnInputType, pgrnOutputType, GrannyDeformPositionNormal); Remplacez par: m_pgrnMeshDeformer = GrannyNewMeshDeformer(pgrnInputType, pgrnOutputType, GrannyDeformPositionNormal, GrannyAllowUncopiedTail); Voilà, nous en avons finis avec la partie Mesh.cpp vous pouvez enregistrer. Maintenant, ouvrez maintenant ModelInstanceUpdate.cpp du même projet. Cherchez: GrannyUpdateModelMatrix(m_pgrnModelInstance, fSecondsElapsed, (const float *) pMatrix, (float *) pMatrix); Remplacez par: GrannyUpdateModelMatrix(m_pgrnModelInstance, fSecondsElapsed, (const float *) pMatrix, (float *) pMatrix, false); Nous en avons aussi finis avec ModelInstanceUpdate.cpp vous pouvez enregistrer. Maintenant, ouvrez Material.cpp du même projet. Cherchez: granny_variant twoSideResult = GrannyFindMatchingMember(pgrnMaterial->ExtendedData.Type, pgrnMaterial->ExtendedData.Object, "Two-sided"); if (NULL != twoSideResult.Type) GrannyConvertSingleObject(twoSideResult.Type, twoSideResult.Object, TwoSidedFieldType, &twoSided); Remplacez par: granny_variant twoSideResult; if (GrannyFindMatchingMember(pgrnMaterial->ExtendedData.Type, pgrnMaterial->ExtendedData.Object, "Two-sided", &twoSideResult) && NULL != twoSideResult.Type) GrannyConvertSingleObject(twoSideResult.Type, twoSideResult.Object, TwoSidedFieldType, &twoSided, NULL); Vous en avez finis avec cette partie aussi, vous pouvez enregistrer. Maintenant, ouvrez ModelInstanceModel.cpp du projet EterGrnLib (toujours le même). Cherchez: return GrannyGetMeshBindingToBoneIndices(m_vct_pgrnMeshBinding[iMeshBinding]); Remplacez par: return (int*)GrannyGetMeshBindingToBoneIndices(m_vct_pgrnMeshBinding[iMeshBinding]); Vous en avez finis avec ModelInstanceModel.cpp et le projet EterGrnLib, vous pouvez enregistrer. Maintenant, allez dans le projet UserInterface et ouvrez UserInterface.cpp. Cherchez: static void GrannyError(granny_log_message_type Type, granny_log_message_origin Origin, char const *Error, void *UserData) { TraceError("GRANNY: %s", Error); } Remplacez par: static void GrannyError(granny_log_message_type Type, granny_log_message_origin Origin, char const* File, granny_int32x Line, char const *Error, void *UserData) { TraceError("GRANNY: %s", Error); } Voilà, vous en avez finis avec les sources client. Vous pouvez recompiler ! Pour finaliser cette installation, rendez vous dans l'archive du début et puis allez dans lib/win32. Prenez granny2.dll et remplacer l'ancien de votre client. Voilà, c'est tout pour ce tuto, je vous remercie de la lecture. En cas de problème, veuillez aller dans la section Aide/Question/Support. Lien de l'archive: (prenez seulement la version 2.9.12 !) ICI (Merci à Liberty de l'avoir upload) Source du code: Metin2Dev Cordialement.
  23. Niveau requis : Débutant Temps estimé : Entre 5 et 10 minutes Réécriture by Xayah Pré-requis: Les sources : HERE (Au préalable vous avez suivi ce tutoriel pour obtenir une VM prête à compiler : Créer un environnement pour Metin2) (Facultatif) Un VDI avec sources pré-installées : VDI by Team FE I. Compiler son Game II. Compiler sa DB Pour continuer : Si vous souhaitez compiler votre lanceur je vous renvoie vers ce tutoriel : Compiler le Client Binary Vous pouvez aussi compiler votre DumpProto qui servira à créer vos protos clients : Compiler & Utiliser un DumpProto Vous pouvez désormais ajouter des systèmes dans les sources : Section Système & Programmation Si vous avez un soucis n'hésitez pas à poster vos demandes d'aide dans l'AQS !
  24. Hellow, Je vous partage un petit "systeme ?" pour choisir le skin de son magasin ==> ICI FE Le tuto est dans le dossier , petit screen de ce petit système a l'ouverture d'un magasin : source : turkmmo feat. iRyZz ^_-
  25. Bonsoir à tous, Je vous propose d'ajouter l’événement Dead à vos quêtes, il pourrait vous être utile ! Pour cela vous devez disposer d'une machine (virtuelle de préférence) pour compiler et des sources du game (il s'agit pour ma part du 40250). Ouvrez quest.h, trouvez la ligne : QUEST_ITEM_INFORMER_EVENT, Et ajoutez ceci juste après : QUEST_DEAD_EVENT, Ouvrez questmanager.cpp, trouvez la ligne : m_mapEventName.insert(TEventNameMap::value_type("item_informer", QUEST_ITEM_INFORMER_EVENT)); Et ajoutez ceci juste après : m_mapEventName.insert(TEventNameMap::value_type("dead", QUEST_DEAD_EVENT)); Ajoutez la fonction ci-dessous dans ce même fichier : void CQuestManager::Dead(unsigned int pc) { PC * pPC; if ((pPC = GetPC(pc))) { if (!CheckQuestLoaded(pPC)) return; m_mapNPC[QUEST_NO_NPC].OnDead(*pPC); } else sys_err("QUEST no such pc id : %d", pc); } Ouvrez questmanager.h, trouvez la ligne : void Kill(unsigned int pc, unsigned int npc); Et ajoutez ceci juste après : void Dead(unsigned int pc); Ouvrez questnpc.cpp et ajoutez la fonction : bool NPC::OnDead(PC& pc) { return HandleReceiveAllEvent(pc, QUEST_DEAD_EVENT); } Ouvrez questnpc.h, trouvez la ligne : bool OnKill(PC& pc); Et ajoutez ceci juste après : bool OnDead(PC& pc); Enfin, ouvrez char_battle.cpp et trouvez la ligne : void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead) Juste après l'ouverture de cette fonction ajoutez : quest::CQuestManager::instance().Dead(GetPlayerID()); Sauvez le tout, compilez et c'est terminé ! Vous pourrez maintenant utiliser l'événement dans vos scripts de quêtes : when dead begin syschat("You are dead.") end Pensez à bien refaire les indentations/tabulations sans quoi la compilation pourrait échouer. Source : metin2dev.org PS : Merci à @ASIKOO, notre IPS converter