• Willkommen im Geoclub - dem größten deutschsprachigen Geocaching-Forum. Registriere dich kostenlos, um alle Inhalte zu sehen und neue Beiträge zu erstellen.

Alle Objekte eines Typs

Charlenni

Geomaster
Hallo,

ich habe mir gestern einige Beispiele aus dem Wherigo-Wiki angeschaut. Dabei ist es mir aufgefallen, dass es immer ein Aufwand ist, alle Objekte eines bestimmten Typs zu ermitteln. Man bekommt mit cartYourCartridge.AllZObjects immer nur alle Objekte, aber schön wäre eine Tabelle cartYourCartridge.AllZItems, die einem nur die Items zurück gibt. Diese gibt es aber nicht. Deshalb habe ich mir dazu etwas überlegt. Vielleicht kann es ja der eine oder andere brauchen.

Als erstes habe ich eine Funktion geschrieben, mit der man den Typ des Objektes ermitteln kann. Diese heißt getObjectType(parObject). Dieser übergibt man ein beliebiges Objekt und bekommt einen nummerischen Wert zurück, der den Typ angibt. Welche Typen welche Nummern haben wird am Anfang des Codeschnippsels definiert. Zusätzlich gibt es noch drei Tabellen, die das Ergebnis von tostring(parObject), einen Namen für das Objekt und die Verknüpfung von Name zu Nummer angeben.

Code:
typeUndefined = 0
typeCartridge = 1
typeZone      = 2
typeCharacter = 3
typeItem      = 4
typeInput     = 5
typeCommand   = 6
typeTask      = 7
typeMedia     = 8
typeObject    = 9

typeString = {"a ZCartridge instance","a Zone instance","a ZCharacter instance","a ZItem instance","a ZInput instance","a ZCommand instance","a ZTask instance","a ZMedia instance","a ZObject instance"}
typeName = {"ZCartridge","Zone","ZCharacter","ZItem","ZInput","ZCommand","ZTask","ZMedia","ZObject"}
typeNumber = {ZCartridge = typeCartridge,Zone = typeZone,ZCharacter = typeCharacter,ZItem = typeItem,ZInput = typeInput,ZCommand = typeCommand,ZTask = typeTask,ZMedia = typeMedia,ZObject = typeObject}

-- Returns the object type as number belonging to parObject
function getObjectType(parObject)
  local iRet = typeUndefined
  for k,v in ipairs(typeString) do
    if Wherigo.NoCaseEquals(tostring(parObject),v) == true then
      iRet = k
    end
  end
  return iRet
end

Nun wird eine Funktion benötigt, die alle Objekte eines Typs oder, noch besser, verschiedener Typen, zurückgibt. Dies geschieht mit der Funktion getObjects(parCartridge,parObjectTypes). parCartridge ist die aktuelle Cartridge (hier im weiteren immer cartYourCartridge genannt) und parObjectTypes der Objekt-Typ, den man haben möchte. Dabei ist der Parameter parObjectTypes eine Tabelle, eine Nummer (oben definiert) oder ein Text (wie z.B. "ZItem"). Warum sehen wir später. Die Funktion hat noch eine kleine Hilfsfunktion hasTableElement(parTable,parElement), die nachschaut, ob die Tabelle parTable das Element parElement enthält.

Code:
-- Returns all objects of types in table parObjectsTypes/of type in number parObjectTypes/of type in string parObjectTypes
function getObjects(parCartridge,parObjectTypes)

  -- If element parElement is in table parTable then true else false
  function hasTableElement(parTable,parElement)
    for k,v in pairs(parTable) do
      if type(v) == "string" then 
        v = typeNumber[v]
      end
      if v == parElement then
        return true
      end
    end
    return false
  end

  local tabReturn = {}
  for k,v in pairs(parCartridge.AllZObjects) do
    if (type(parObjectTypes) == "table") and hasTableElement(parObjectTypes,getObjectType(v)) then
        table.insert(tabReturn,v)
    end
    if (type(parObjectTypes) == "number") and (getObjectType(v) == parObjectTypes) then
        table.insert(tabReturn,v)
    end
    if (type(parObjectTypes) == "string") and (getObjectType(v) == typeNumber[parObjectTypes]) then
        table.insert(tabReturn,v)
    end
  end
  return tabReturn
end

Wie wird nun diese Funktion angewendet? Als Beispiel habe ich hier einen Aufruf um alle Items zu ermitteln.

Code:
-- Example, how to call getObjects()  
function getAllItems()
  local tabItems = {}
  tabItems = getObjects(cartYourCartridge,{typeItem})
  tabItems = getObjects(cartYourCartridge,typeItem)
  tabItems = getObjects(cartYourCartridge,"ZItem")
  return tabItems
end

Alle drei Aufrufe liefern das gleiche Ergebnis. Warum wird nun aber eine Tabelle als Parameter verwendet? Nun, man kann dort auch mehrere Typen angeben. Möchte man z.B. alle Items und Characters haben, dann lautet der Aufruf wie folgt:

Code:
-- Example, how to call getObjects()  
function getCharactersAndItems()
  local tabReturn = {}
  tabReturn = getObjects(cartYourCartridge,{typeCharacter,typeItem})
  tabReturn = getObjects(cartYourCartridge,{"ZCharacter","ZItem"})
  tabReturn = getObjects(cartYourCartridge,{"ZCharacter",typeItem})
  return tabReturn
end

Und wieder gibt die Funktion immer das gleiche Ergebnis zurück. Was die Funktion zurückgibt kann man wie cartYourCartridge.AllZObjects verwenden, allerdings enthält die Tabelle nur die gewünschten Objekt-Typen.

Die Ergebnis-Tabelle ist der Reihe nach geordnet. So erhält man z.B. mit getObejects(cartYourCartridge,{typeItem})[1] das erste erzeugte Item. Manchmal weiß man aber nur den Namen des Objektes und nicht seine Nummer, würde also gerne das zugehörige Objekt zum Namen erfahren. Dies lässt sich mit der nächsten Funktion erreichen. Diese erzeugt eine Tabelle, bei der man über den Namen des Objektes auf das Objekt zugreifen kann.

Code:
-- Returns all objects of types in table parObjectsTypes/of type in number parObjectTypes/of type in string parObjectTypes with name as index
-- Cave! If you have more objects with same name, only the las will be in result
function getObjectsByName(parCartridge,parObjectTypes)

  -- If element parElement is in table parTable then true else false
  function hasTableElement(parTable,parElement)
    for k,v in pairs(parTable) do
      if type(v) == "string" then 
        v = typeNumber[v]
      end
      if v == parElement then
        return true
      end
    end
    return false
  end

  local tabReturn = {}
  for k,v in pairs(parCartridge.AllZObjects) do
    if (type(parObjectTypes) == "table") and hasTableElement(parObjectTypes,getObjectType(v)) then
        tabReturn[v.Name] = v
    end
    if (type(parObjectTypes) == "number") and (getObjectType(v) == parObjectTypes) then
        tabReturn[v.Name] = v
    end
    if (type(parObjectTypes) == "string") and (getObjectType(v) == typeNumber[parObjectTypes]) then
        tabReturn[v.Name] = v
    end
  end
  return tabReturn
end

Sieht ganz ähnlich aus wie die erste Abfrage, erzeugt aber eine andere Tabelle. Mit dieser kann man z.B. anschließend, wenn man ein Item hat, das "Fahrrad" heißt, folgendermaßen auf das zugehörige Objekt zugreifen

Code:
objFahrrad = getObjectsByName(cartYourCartridge,typeItem)["Fahrrad"]

Und schon kann man alles möglich mit dem Objekt objFahrrad anstellen.

Um nun nicht immer diese langen Aufrufe zu verwenden habe ich mir überlegt, am Anfang der Cartridge, also in OnStart() und OnResume() die Ergebnisse einfach abzulegen. Dies erreiche ich mit dem Aufruf der folgenden Funktion, deren Parameter das Cartridge-Objekt ist (also initObjects(cartYourCartridge)).

Code:
-- Set all entries for cartridge
-- Cave! If you change the name of an object, you have to call this function again (because of GetObjectsByName)
function initObjects(parCartridge)
  -- Get all
  parCartridge.AllZones = getObjects(parCartridge,typeZone)
  parCartridge.AllZCharacters = getObjects(parCartridge,typeCharacter)
  parCartridge.AllZItems = getObjects(parCartridge,typeItem)
  parCartridge.AllZInputs = getObjects(parCartridge,typeInput)
  parCartridge.AllZCommands = getObjects(parCartridge,typeCommand)
  parCartridge.AllZTasks = getObjects(parCartridge,typeTask)
  parCartridge.AllZMedia = getObjects(parCartridge,typeMedia)
  -- Get all by name
  parCartridge.AllZObjectsByName = getObjectsByName(parCartridge,{typeCartridge,typeZone,typeCharacter,typeItem,typeInput,typeCommand,typeTask,typeMedia,typeObject})
  parCartridge.AllZonesByName = getObjectsByName(parCartridge,typeZone)
  parCartridge.AllZCharactersByName = getObjectsByName(parCartridge,typeCharacter)
  parCartridge.AllZItemsByName = getObjectsByName(parCartridge,typeItem)
  parCartridge.AllZInputsByName = getObjectsByName(parCartridge,typeInput)
  parCartridge.AllZCommandsByName = getObjectsByName(parCartridge,typeCommand)
  parCartridge.AllZTasksByName = getObjectsByName(parCartridge,typeTask)
  parCartridge.AllZMediaByName = getObjectsByName(parCartridge,typeMedia)
  -- If you want, you could set properties Type and TypeName for all objects
  for k,v in pairs(parCartridge.AllZObjects) do
    v.Type = getObjectType(v)
    v.TypeName = typeName[v.Type]
  end
end

Man hat dann mit cartYourCartridge.AllZItems eine Tabelle, die alle Items enthält. Parallel dazu gibt es auch eine cartYourCartridge.AllZItemsByName, die alle Items enthält und auf die mit dem Namen zugegriffen werden kann. Damit wäre cartYourCartridge.AllZItems["Fahrrad"] das Item, das den Namen Fahrrad trägt. Kein langes Suchen, keine For-Schleifen.

Zu beachten ist, dass sich Namen im Programmablauf ändern können. Dann muss die entsprechende Tabelle neu erzeugt werden. Genauso, wenn Objekte während der Laufzeit erzeugt werden.

Als kleinen Gimmick habe ich eingebaut, dass der Typ des Objekts gleich dort mit abgelegt wird. Man kann darauf mit der Eigenschaft Type bzw. TypeName zugreifen, also object.Type (numerisch) bzw. object.TypeName (Zeichenkette wie z.B. "ZItem"). Kann man, muss man nicht.

Getestet habe ich es erstmal nur im Emulator. Dort lief es aber ohne Probleme. Und da nichts verwendet wird, was man nicht in vielen Cartridges schon verwendet hat, denke ich nicht, dass es bei den anderen Playern zu Problemen kommt.

Anbei noch die Lua-Datei, damit man nicht alles zusammenkopieren muss.

Wie binde ich es ein? Einfach den Inhalt der angehängten Lua-Datei in "Author Scripts" kopieren und in OnStart() und OnResume() die Funktion initObjects(cartYourCartridge) aufrufen, wobei Ihr cartYourCartridge durch Euren Cartridge-Namen ersetzten müsst. Fertig.

Wenn jemand noch einen Fehler findet, oder noch viel besser, einen Verbesserungsvorschlag hat: immer raus damit. Und wenn es sowas schon gibt, ich es aber auf Seite x des Forums übersehen habe (lese hier erst seit ein paar Tagen mit), dann auch raus damit, damit ich es mir anschauen kann.

Ansonsten viele Grüße und weiterhin viel Spaß
Dirk (Charlenni)
 

Anhänge

  • getObjects.zip
    1,2 KB · Aufrufe: 16
OP
C

Charlenni

Geomaster
So, noch kurz überarbeitet.

Den Funktionsaufruf von getObjects() überarbeitet. Damit ist die Übergabe der Parameter egal. Die Funktion kann jetzt wie folgt aufgerufen werden.

Code:
getObjects(cartYourCartridge,typeItem)
getObjects(cartYourCartridge,"ZItem")
getObjects(cartYourCartridge,4)
getObjects(cartYourCartridge,{typeItem,typeCharacter})
getObjects(cartYourCartridge,{"ZItem",typeCharacter})
getObjects(cartYourCartridge,typeItem,"ZCharacter")
getObjects(cartYourCartridge,typeItem,typeCharacter,typeZone)
getObjects(cartYourCartridge,{typeItem,typeCharacter,typeZone})
getObjects(cartYourCartridge,{typeItem,typeCharacter},typeZone)
Usw. Es ist also unerheblich, wieviele Argumente Ihr angebt oder ob diese doppelt vorkommen. Auch die Art der Argumente (Tabelle, Text, Wert) ist unerheblich. Können auch beliebig gemischt werden. Und geschachtelt.

Es ist noch eine globale Funktionen hinzugekommen. hasTableElement(parTable,parElement) schaut nach, ob das Element parElement in der Tabelle parTable enthalten ist. Wenn ja, dann wird true ansonsten false zurückgegeben. Kann man vielleicht auch an anderer Stelle brauchen.

In initObjects() (das nicht zwingend aufgerufen werden muss, wenn man die statischen Tabellen cartYourCartridge.AllZItems usw. nicht benutzen möchte) wurden noch die Eigenschaften "Type" und "TypeName" durch "ObjectType" und "ObjectTypeName" ersetzt, da "Type" schon bei Characters verwendet wird.

Vieleicht ist noch ein Anwendungsbeispiel hilfreich.

Wenn Ihr die Namen aller Items und Characters ausdrucken möchtet, dann geht das wie folgt.

Code:
for k,v in ipairs(getObjects(cartYourCartridge,"ZItem","ZCharacter")) do
  print(k,v.Name)
end
Dies funktioniert auch, wenn Ihr initObjects() nicht aufruft.
 

Anhänge

  • getObjects.zip
    1,3 KB · Aufrufe: 16

bodenseepingu

Geomaster
Du musst auf verschiedenen Geräten bisschen aufpassen - ich hab da irgendwas in der Erinnerung, dass nicht auf jedem Gerät jeder Objekttyp gleich ist.

Bin mir da aber absolut nicht sicher was für ein Typ es war - kann sein dass auf manchen Geräten ZMedia als ZObject gelistet wird oder so.
 
OP
C

Charlenni

Geomaster
Ist auf dem OpenWIG (und damit auch beim WhereYouGo). Dort wird nicht zwischen ZCharacter und ZItem unterschieden. Auch die anderen Objekte könnten dort Schwierigkeiten machen. Sobald ich wieder ein Andoidgerät in den Händen habe, werde ich mal nachschauen.

Ansonsten habe ich mittlerweile auch cartridge.GetAllOfType() gefunden, das aber auf den oben aufgeführten Geräten auch nicht existiert. Allerdings ist die hier gezeigte Lösung weit variabler, da verschiedene Objekttypen gleichzeitig ermittelt werden können.
 
Oben