Liste des ressources récoltables

Inscrit
13 Mars 2022
Messages
24
Reactions
3
#1
Bonjour,

But:
J'essaie de réunir la liste de toutes les ressources récoltables par les métiers de récolte avec leur mapid et leur cellid.

Où j'en suis:
J'ai donc remonté la piste par la liste des éléments que l'on peut trouver sur les maps pour composer le décor.
Dans chaque mapid.dlm (situé dans les mapsX.d2p), on trouve la liste des éléments affichés à l'écran et ceux qui possèdent un identifier sont ceux avec lesquels on peut intéragir.
ici un exemple d'une cellule d'un mapid.json:
{
          "cellId": 415,
          "elementsCount": 2,
          "elements": [
            {
              "elementName": "Graphical",
              "elementId": 54428,
              "hue_1": 0,
              "hue_2": 0,
              "hue_3": 0,
              "shadow_1": 14,
              "shadow_2": 2,
              "shadow_3": -1,
              "offsetX": 0,
              "offsetY": 0,
              "altitude": 0,
              "identifier": 0
            },
            {
              "elementName": "Graphical",
              "elementId": 57446,
              "hue_1": -50,
              "hue_2": -54,
              "hue_3": -73,
              "shadow_1": 27,
              "shadow_2": 10,
              "shadow_3": 0,
              "offsetX": 28,
              "offsetY": -71,
              "altitude": 0,
              "identifier": 534720
            }
          ]
        },
On peut ensuite retrouver chacun des éléments dans élément.ele avec un type:3 pour entityElement. Les éléments de type 3 n'ont pas de gfx mais un
"entity_look": "{2205}" de ce genre à la place. Je n'arrive pas à remonter plus la piste pour trouver à quoi correspond cet entity_look et ensuite pouvoir déterminer de quelle ressource il s'agit.

---
Si vous avez une idée qui me permettrait comprendre où trouver ces entity_look ou comment retrouver la liste des ressources pour chaque mapid/cellid, je vous en serais reconnaissant.

Merci d'avance
Roro
 
Inscrit
13 Mars 2022
Messages
24
Reactions
3
#2
j'aurai peut etre du mettre ça dans question/réponse. mb
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#3
il te faut le gfx de la resource.
Voici comment avoir tous les gfx de la map :

Python:
    def computeGfxList(self, skipBackground=False, layersFilter=[]) -> list[NormalGraphicalElementData]:
        gfxList = {}
        self._gfxCount = {}
        numLayer = len(self.layers)
        for l in range(numLayer):
            layer = self.layers[l]          
            if layersFilter and layer.layerId not in layersFilter:
                continue
            layer.layerId
            if not(skipBackground and l == 0):
                lsCell = layer.cells
                numCell = len(lsCell)
                for c in range(numCell):
                    cell = lsCell[c]
                    lsElement = cell.elements
                    numElement = len(lsElement)
                    for e in range(numElement):
                        element = lsElement[e]
                        if element.elementType == ElementTypesEnum.GRAPHICAL:
                            element = cast(GraphicalElement, element)
                            elementId = element.elementId
                            elementData = Elements().getElementData(elementId)
                            if elementData is None:
                                Logger().error("Error: Unknown graphical element ID " + str(elementId))
                            elif isinstance(elementData, NormalGraphicalElementData):
                                graphicalElementData = elementData
                                self._gfxCell[graphicalElementData.gfxId] = cell.cellId
                                gfxList[graphicalElementData.gfxId] = graphicalElementData
                                if graphicalElementData.gfxId in self._gfxCount:
                                    self._gfxCount[graphicalElementData.gfxId] += 1
                                else:
                                    self._gfxCount[graphicalElementData.gfxId] = 1
        self._gfxList = list(gfxList.values())
        return self._gfxList
Voici comment tu récupère maintenant le path sur ton pc du gfx de l'element:
Python:
    def getGrphicalElementGfxUri(self, nged: NormalGraphicalElementData) -> Uri:
        isJpg = Elements().isJpg(nged.gfxId)
        path_str = (
            self._gfxPath
            + "/"
            + ("jpg" if isJpg else "png")
            + "/"
            + str(nged.gfxId)
            + "."
            + ("jpg" if isJpg else self._extension)
        )
        return Uri(path_str, nged.gfxId)
GFX path ici est
self._gfxPath = LangManager().getEntry("config.gfx.path.cellElement") tu peux trouver sa valeur dans le fichier config.xml.
Maintenant load l'image de l'élement et regarde à quoi il ressemble => construit toi une table qui matche le gfx id avec l'id de la resource et c'est bon.
La suite c'est parcourir les maps, parcourir les elements de ces maps et matcher les resources en utilisant les gfxId avec ta table
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#4
Voici un code rapide que j'ai fait qui fait ce que tu veux:

Python:
    CURR_DIR = os.path.dirname(os.path.abspath(__file__))
    elementsIndexPath = Uri(LangManager().getEntry("config.atouin.path.elements")).toFile()
    with open(elementsIndexPath, "rb") as fp:
        ElementsAdapter().getResource(None, fp.read())
    def getGfxListOfMap(mapId):
        mapData = MapLoader.load(mapId)
        return mapData.computeGfxList(True, layersFilter=[Layer.LAYER_DECOR])
    for mapId in listOfMapIds:
        for nged in getGfxListOfMap(mapId):
            gfxId = nged.gfxId
            print(gfxId)
            p2p = PakProtocol2()
            p2p.initStream(Uri(GFX_PATH))
            isJpg = Elements().isJpg(gfxId)
            extension = "jpg" if isJpg else "png"
            path_str = f"{GFX_PATH}/{extension}/{gfxId}.{extension}"
            gfxUri = Uri(path_str, gfxId)
            image_bytes = p2p.loadDirectly(gfxUri)
            image = Image.open(io.BytesIO(image_bytes))
            image.show()
Tu trouveras le copde de Elements, ElementsAdapter, PakProtocol2 et computeGfxList dans le repo : pydofus2.
Tu trouveras lescodes similaires en .AS dans la décompilation du code source du jeu
 
Inscrit
13 Mars 2022
Messages
24
Reactions
3
#5
Merci pour ta réponse,

j'avoue que je n'ai pas tout compris. J'ai effectivement décompiler tous les GFX sur mon PC. j'ai reussi à faire fonctionner le dernier code que tu m'as fourni jusqu'au print auquel j'ai ajouté l'id
Python:
print(nged.id,gfxId)
.

Je remarque cependant que les ressources récoltables ne sont pas affichés (en tout cas ce que je suspecte être les ressources récoltables)

Je pense que le problème vient de cette ligne
Python:
elif isinstance(elementData, NormalGraphicalElementData):
qui filtre les éléments pour ne garder que les éléments "normaux" or je pense que les ressources se trouvent dans les "EntityGraphicalElement"

Ces éléments n'ont d'ailleurs pas de gfx_id puisqu'à la place ils ont des "entity_look"

Si tu as une idée de comment retrouver à quoi cela correspond pour que je puisse ensuite faire le tableau de correspondance j'en serai ravis :)

d'autres parts si tu as un lien qui me permettrait de faire fonctionner le typing sur python, je suis intéressé. Je n'ai jamais réussi à l'exécuter (j'ai du retirer tous les types à la main :( ).

Merci d'avance
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#6
Je doute qu'ils n'aient pas de gfx mais je vais voir.
Si non faut rentrer dans le code et comprendre d'ou sort l'image qui represente la resource.
Il doit bien y avoir un ID et une image associée il faut juste un peu de temps et de patience jusqu'à trouver c'est quoi.
Dans le code que je t'ai envoyé je filtre aussi le ground `
return mapData.computeGfxList(True, layersFilter=[Layer.LAYER_DECOR])
`
Car je l'ai utilisé à l'origine pour cartographier les éléments de décores et leur relation avec les indices de chasses au trésor.
Il faut peut être qu tu enlève ce filtre et que tu affiches tous les eleemnts qui ont un gfx dans une seule fenêtre.
Ce code ne doit pas être difficile à faire surtout si tu uitilise pydofus2
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#7
Hello je viens de pousser un commit sur pydofus2 ou j'ai mis une gemme qui va te plaire.
C'est un outil au quel tu donnes l'id d'une map et ile te show dans une fenêtre tous les graphical elements et quand tu hover au dessu de l'un d'eux à partir d'un moment tu verra son gfxId et en click dessu avec button gauche le gfxId se copiera dans ton clip
Je l'ai mis sous le dossier pydofus2/utilities.
Voici son code :

Python:
import io
import sys

from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import (QApplication, QErrorMessage, QGraphicsPixmapItem,
                             QGraphicsScene, QGraphicsView, QMainWindow)

from pydofus2.com.ankamagames.atouin.AtouinConstants import AtouinConstants
from pydofus2.com.ankamagames.atouin.data.elements.Elements import Elements
from pydofus2.com.ankamagames.atouin.data.elements.subtypes.NormalGraphicalElementData import \
    NormalGraphicalElementData
from pydofus2.com.ankamagames.atouin.data.map.Cell import Cell
from pydofus2.com.ankamagames.atouin.data.map.Layer import Layer
from pydofus2.com.ankamagames.atouin.managers.MapDisplayManager import \
    MapDisplayManager
from pydofus2.com.ankamagames.atouin.resources.adapters.ElementsAdapter import \
    ElementsAdapter
from pydofus2.com.ankamagames.jerakine.data.I18nFileAccessor import \
    I18nFileAccessor
from pydofus2.com.ankamagames.jerakine.managers.LangManager import LangManager
from pydofus2.com.ankamagames.jerakine.resources.adapters.AdapterFactory import \
    AdapterFactory
from pydofus2.com.ankamagames.jerakine.resources.events.ResourceEvent import \
    ResourceEvent
from pydofus2.com.ankamagames.jerakine.resources.loaders.ResourceLoaderFactory import \
    ResourceLoaderFactory
from pydofus2.com.ankamagames.jerakine.resources.loaders.ResourceLoaderType import \
    ResourceLoaderType
from pydofus2.com.ankamagames.jerakine.types.Uri import Uri

I18nFileAccessor() #This will load the i18n files don't forget to do it
GFX_PATH = LangManager().getEntry("config.gfx.path.cellElement")

class MainWindow(QMainWindow):
    _startWidth = 1280
    _startHeight = 1024
  
    def __init__(self, mapId, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setObjectName("sharedStage")
        self.setWindowTitle('Map Renderer')
        self.stage = QGraphicsView(self)
        self.worldContainer = MapRenderer(mapId, self)     
        self.stage.setScene(self.worldContainer)
        self.stage.setGeometry(0, 0, self._startWidth, self._startHeight)
        self.stage.setObjectName("stage")
        MAP_IMAGE_WIDTH = AtouinConstants.CELL_WIDTH * AtouinConstants.MAP_WIDTH + AtouinConstants.CELL_WIDTH
        AtouinConstants.WIDESCREEN_BITMAP_WIDTH = MAP_IMAGE_WIDTH
        self.worldContainer.setSceneRect(0, 0, AtouinConstants.WIDESCREEN_BITMAP_WIDTH, MainWindow._startHeight)
        self.setCentralWidget(self.stage)
        QTimer.singleShot(0, self.worldContainer.loadElementsFile)
        self.show()

class MapRenderer(QGraphicsScene):
  
    def __init__(self, mapId, parent) -> None:
        super(MapRenderer, self).__init__(parent)
        MapDisplayManager().loadMap(mapId)
        self.error_dialog = QErrorMessage(parent)
        self.gfx_loader = ResourceLoaderFactory.getLoader(
            ResourceLoaderType.PARALLEL_LOADER
        )
        self.gfx_loader.on(ResourceEvent.ERROR, self.onloadError)
        self.gfx_loader.on(ResourceEvent.LOADED, self.onGfxsLoaded)
        self.gfx_loader.on(ResourceEvent.LOADER_COMPLETE, self.onLoadComplete)
        self.gfx_loader.on(ResourceEvent.LOADER_PROGRESS, self.onMapLoadingProgress)
        self.mousePressEvent = self.onSceneMousePress
        self.elemCount = 0

    def onSceneMousePress(self, event):
        item = self.itemAt(event.scenePos(), self.parent().stage.transform())
        if isinstance(item, QGraphicsPixmapItem):
            if event.button() == Qt.LeftButton:
                gfxId = item.toolTip()
                clipboard = QApplication.clipboard()
                clipboard.setText(gfxId)
            elif event.button() == Qt.RightButton:
                item.setVisible(False)

    def onMapLoadingProgress(self, event, uri, total, loadedCount):
        pass 
      
    def getGfxUri(self, gfxId) -> Uri:
        isJpg = Elements().isJpg(gfxId)
        path_str = (
            GFX_PATH
            + "/"
            + ("jpg" if isJpg else "png")
            + "/"
            + str(gfxId)
            + "."
            + ("jpg" if isJpg else "png")
        )
        return Uri(path_str, gfxId)
  
    def onGfxsLoaded(self, event, uri: Uri, resourceType, image_bytes: io.BytesIO):
        grid_columns = 10  # Number of columns in the grid
        witem_size = 1.5 * AtouinConstants.CELL_WIDTH  # Size of each item in the grid
        hitem_size = 1.5 * AtouinConstants.CELL_HEIGHT
        margin = 20  # Margin between items
      
          
        pixmap = QPixmap()
        pixmap.loadFromData(image_bytes)
        nged: NormalGraphicalElementData = uri.tag
        cellId = MapDisplayManager().dataMap.getGfxCell(nged.gfxId)
        imageCenter = Cell.cellPixelCoords(cellId)
      
        pixmapItem = QGraphicsPixmapItem(pixmap)  # Create QGraphicsPixmapItem
        pixmapItem.setToolTip(str(nged.gfxId))
      
        # Calculate position in the grid
        row = self.elemCount // grid_columns
        col = self.elemCount % grid_columns
      
        # Calculate position of the item
        x = col * (witem_size + margin)
        y = row * (hitem_size + margin)
        pixmapItem.setPos(x, y)
      
        # pixmapItem.setPos(imageCenter.x - nged.origin.x + nged.size.x, imageCenter.y - nged.origin.y + nged.size.y)
        pixmapItem.setScale(min(witem_size / pixmap.width(), hitem_size / pixmap.height()))
        pixmapItem.setVisible(True)
        self.elemCount += 1

        self.addItem(pixmapItem)  # Add item to scene
      
    def onLoadComplete(self, event, total, completed):
        pass
              
    def onloadError(self, event, uri, errorMsg, errorCode):
        error = f"Load of resource at uri: {uri} failed with err[{errorCode}] {errorMsg}"
        self.error_dialog.showMessage(error)
        QApplication.instance().quit()
      
    def onElementsLoaded(self, event, uri, resourceType, resource):
        for nged in MapDisplayManager().dataMap.computeGfxList(False, layersFilter=[]):
            if nged.gfxId:
                uri = self.getGfxUri(nged.gfxId)
                uri.tag = nged
                self.gfx_loader.load(uri)     
      
    def loadElementsFile(self) -> None:
        AdapterFactory.addAdapter("ele", ElementsAdapter)
        elementsIndexPath = LangManager().getEntry("config.atouin.path.elements")
        elementsLoader = ResourceLoaderFactory.getLoader(
            ResourceLoaderType.SINGLE_LOADER
        )
        elementsLoader.on(ResourceEvent.ERROR, self.onloadError)
        elementsLoader.on(ResourceEvent.LOADED, self.onElementsLoaded)
        elementsLoader.load(Uri(elementsIndexPath))

if __name__ == "__main__":
    mapId = 154010883
    app = QApplication(sys.argv)
    main_window = MainWindow(mapId)
    sys.exit(app.exec_())
 
Dernière édition:
Inscrit
10 Février 2017
Messages
26
Reactions
43
#8
Merci pour ta réponse,
d'autres parts si tu as un lien qui me permettrait de faire fonctionner le typing sur python, je suis intéressé. Je n'ai jamais réussi à l'exécuter (j'ai du retirer tous les types à la main :( ).
Merci d'avance
Tu utilises quelle version de python ?
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#9
J'ai testé de ma part et je n'ai pas trouver de gfx dans les maps qui correspondent aux resources.
Ca vient des messages envoyés par le serveur ... Non le serveur envoie juste "elementId" et "elementTypeId",
Donc ca reste très faisable car tout est dans les données en local.
Ceci complique beaucoup la tâche
 
Dernière édition:
Inscrit
13 Mars 2022
Messages
24
Reactions
3
#10
J'ai testé de ma part et je n'ai pas trouver de gfx dans les maps qui correspondent aux resources.
Ca vient des messages envoyés par le serveur ... Non le serveur envoie juste "elementId" et "elementTypeId",
Donc ca reste très faisable car tout est dans les données en local.
Ceci complique beaucoup la tâche
J'ai effectivement déjà un module qui récupère les ressources présentes sur la map au moment de l'arrivée. Mais le fait de pas avoir les ressources des maps sur lesquelles je ne suis jamais allé me dérange.

J'ai essayé de comprendre à quoi été relier ce "entity_look" associé au EntityElement mais je ne vois pas ou ça va.
Cela dit, j'ai aucun mal à comprendre la partie communication et paquet mais j'ai un peu plus de mal pour savoir comment fonctionne l'application elle même.
Je n'ai clairement pas tes connaissances de dev. Chapeau pour ton code!
Ça me fait penser qu'avec un peu de chance je devrais trouver comment avoir les prix moyens des items avec ton code ;)
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#12
En fait il y'a des normalgraphical element qui sont des AnimatedGraphicalElements et dans ce cas ils ont

Moi j'utilise 3.9.11 peut être ca vient de cela le fait que ta version de python ne comprend pas la syntaxe def(x: T) ou x: T = v
 
Dernière édition:
Inscrit
13 Mars 2022
Messages
24
Reactions
3
#13
Ah je ne les ai encore pas vu

Effectivement ça semble être ajouté à 3.9
 
Inscrit
10 Février 2017
Messages
26
Reactions
43
#14
V
Ah je ne les ai encore pas vu

Effectivement ça semble être ajouté à 3.9
NVM tous les éléments récoltables sont des EntityGraphicalElementData et dans ce cas effectivement c'est l'entity look qui porte l'info sur l'apparence. par exemple je viens de voir en faisant des test que le entity look du Nettle (resource recoltable au niveau 1 alchimiste) a un entity look b'{3212}'.
Ce que tu peux faire déjà c'est suivre ce code et voir si l'entity look est constant pour les ressources au travers les maps. Ca doit être le cas et du coup il ne reste qu'à parcourir les maps ou il y'a les ressources et construire une table qui match le entity look avec la ressource et t'es bon.

J'ai fait un codeàqui prend une map, un element Id et te donne son entity look.

Python:
import os
from typing import cast
from pydofus2.com.ankamagames.atouin.data.elements.Elements import Elements
from pydofus2.com.ankamagames.atouin.data.elements.subtypes.EntityGraphicalElementData import EntityGraphicalElementData
from pydofus2.com.ankamagames.atouin.data.map.elements.GraphicalElement import \
    GraphicalElement
from pydofus2.com.ankamagames.atouin.data.map.Map import Map
from pydofus2.com.ankamagames.atouin.enums.ElementTypesEnum import \
    ElementTypesEnum
from pydofus2.com.ankamagames.atouin.resources.adapters.ElementsAdapter import \
    ElementsAdapter
from pydofus2.com.ankamagames.jerakine.data.I18nFileAccessor import \
    I18nFileAccessor
from pydofus2.com.ankamagames.jerakine.logger.Logger import Logger
from pydofus2.com.ankamagames.jerakine.managers.LangManager import LangManager
from pydofus2.com.ankamagames.jerakine.resources.loaders.MapLoader import \
    MapLoader
from pydofus2.com.ankamagames.jerakine.types.Uri import Uri


def findEntitylookOfAElement(game_map: Map, elemntId):
    for layerIdx, layer in enumerate(game_map.layers):
        if layerIdx != 0:
            for cell in layer.cells:
                for element in cell.elements:
                    if element.elementType == ElementTypesEnum.GRAPHICAL:
                        element = cast(GraphicalElement, element)
                        elementData = Elements().getElementData(element.elementId)
                        if isinstance(elementData, EntityGraphicalElementData):
                            if element.identifier == elemntId:
                                print("Element look: " + str(elementData.entityLook))
                                return elementData.entityLook, cell.cellId

I18nFileAccessor()
GFX_PATH = LangManager().getEntry("config.gfx.path.cellElement")
Logger().activateConsolLogging()

if __name__ == "__main__":
    CURR_DIR = os.path.dirname(os.path.abspath(__file__))
    elementsIndexPath = Uri(GFX_PATH).toFile()
    with open(elementsIndexPath, "rb") as fp:
        ElementsAdapter().getResource(None, fp.read())

    mapId = 153880321
    elemntId = 489692
    game_map = MapLoader.load(mapId)
    elementLook, entity_cellId = findEntitylookOfAElement(game_map, elemntId)
Tu peux le modifier pour qu'il retrouve ton elment grace à sa cellId. Comme ca t'as pas besoin de sniffer.
 
Dernière édition:
Inscrit
10 Février 2017
Messages
26
Reactions
43
#15
le 3212 dans b'{3212}' est le boneId de l'entity. pour trouver comment le client retrouve l'apparence depuis cet Id tout est dans
BoneIndexManager => scripts\com\ankamagames\tiphon\engine\BoneIndexManager.as

Tu peux retrouver dedans comment toi même extraire de ton pc l'apparence de l'entity.

Update:

C'est ici ou se trouvent tous les bones //content/gfx/sprites/bones0.d2p
C'est des .d2p donc tu sais déjà comment les unpack.

Là je pense que t'as tout ce qu'il te faut
 
Dernière édition:
Inscrit
13 Mars 2022
Messages
24
Reactions
3
#16
le 3212 dans b'{3212}' est le boneId de l'entity. pour trouver comment le client retrouve l'apparence depuis cet Id tout est dans
BoneIndexManager => scripts\com\ankamagames\tiphon\engine\BoneIndexManager.as

Tu peux retrouver dedans comment toi même extraire de ton pc l'apparence de l'entity.

Update:

C'est ici ou se trouvent tous les bones //content/gfx/sprites/bones0.d2p
C'est des .d2p donc tu sais déjà comment les unpack.

Là je pense que t'as tout ce qu'il te faut
Génial, merci beaucoup !
 
Inscrit
31 Octobre 2020
Messages
10
Reactions
7
#17
hellooo,

Je reviens sous ce post parce que c'est le seul principalement qui parle des elements interactifs haha,

j'ai un petit souci, alors c'est assez bizarre, j'ai les éléments sur la map, je peut interagir avec, changer leurs state quand j'utilise l'element comme par exemple du blé mais bizarrement, une fois la cell du blé utilisé et donc normalement en state "Inactive", et bien je peut tout de même la récolter, il n'y a pas l'indication comme quoi l'element est inutilisable car en en repousse par exemple (indication de couleur orange je crois).

Malheureusement je ne peut envoyé une vidéo montrant mon problème ici, si quelqu'un aurait une idée par hasard, je suis dispo pour détailler mon problème en privé ou sur discord si besoins !

merci d'avance :teeth:
 
Haut Bas