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.
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.
Wie wird nun diese Funktion angewendet? Als Beispiel habe ich hier einen Aufruf um alle Items zu ermitteln.
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:
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.
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
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)).
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)
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)