hoping for help on how to get the 'attr' syntax to work with lists and dictionaries

(filler for getting my edited post, updated/posted)


This 'attr' syntax/format is very convient (quick/easy typing/copy-paste/etc):

<attr name="NAME_OF_ATTRIBUTE" type="TYPE_OF_ATTRIBUTE">CONTENT</attr>

<attr name="NAME_OF_ATTRIBUTE" type="TYPE_OF_ATTRIBUTE">
  CONTENT
</attr>

however, with the older versions of quest (does Pixie's new versions fix this?):

it doesn't work with List and Dictionary Attributes, having to instead, use this much less convient syntax/format:

<NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE type="TYPE_OF_ATTRIBUTE">
  CONTENT (depends on Attribute Type): <value><value>, <item><key></key><value></value></item>, <item key="XXX">SCRIPTING</item>
</NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE>

being able to do:

<attr name="NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE" type="TYPE_OF_ATTRIBUTE">
  CONTENT (depends on Attribute Type): <value><value>, <item><key></key><value></value></item>, <item key="XXX">SCRIPTING</item>
</attr>

...would be so much better and appreciated...


does anyone know how (if able) to get the 'attr' syntax/format to work for Lists/Dictionaries, if it's possible by altering the 'core' aslx code files, or is it within the 'C# and etc' coding?

I'd very much like to do this, instead of having to change all of my lists/dictonaries (as I didn't realize the 'attr' syntax didn't work for them), and I really hate if I had to use the 'NAME_OF_ATTRIBUTE' instead of 'attr', as it's really messy and hard to read and has a ton of other very inconvient issues with it, compared to having the 'attr' syntax/format...


I'm just asking this now, before I start going through all of my lists/dictionaries to change them over to the inferior syntax/format, as opposed to hoping that it can be done and that someone will help with how to do it, to change the lists/dictionaries over to using the 'attr' syntax/fomat...

I'll also try to see if I can figure it out myself too... but I'm not too confident in dealing with the core aslx and/or especially if it uses the 'C# or whatever'... as I've still not delved into learning 'C#' or whatever other coding it uses.


I have a feeling you cannot. Usually Quest will use the attribute name as the XML tag name, so:

<myattribute>Some text</myattribute>

It only uses attr when there is a space in the name, because XML does not allow spaces in the tag name.

<attr="my attribute">Some text</attr>

However, if you have a list of dictionary attribute with a space, you will get an error when you try to save the game. Why that should be I do not know... which does not really help, I guess.


K.V.

It only uses attr when there is a space in the name, because XML does not allow spaces in the tag name.

I was recently wondering about that.


In HK's example:

Couldn't you just delete attr from each of the tags which are dictionaries or lists?

If so, you could use Find & Replace in code view, making sure to pay attention before replacing anything.

find:
<attr
replace with:

<

...well... You'd have to fool with removing the quotation marks from each dictionary (or list) name, too...


@ KV:

it's a bit of a pain, but I can do the work/pain and convert my code (there's a lot of code/lists/dictionaries... lots of compartmentalized/encapsulation, as separate files)

the bigger issue for myself, is that the 'attr' syntax/format is so much better, for a lot of various human-friendly usage reasons


I'd prefer using the 'attr', but it looks like it won't be possible... as it's probably too connected/buried with whatever of the various internal coding of quest and/or other code languages/systems/softwares it issues (dll, that 'flee' code stuff, and etc)


@ Pixie:

thanks for the reply, I tried looking through the core aslx files as well as some of the source code, but couldn't find anything in my limited search attempt dealing with any code that deals with the syntax format structure

do you any chance possibly know of where it can be found? (and I can try to see if I could possibly figure out how to edit it... riiight, lol)) or is it using code with the dll files (can't remember if the source code is what's in the dll files or if they're different --- can't remember, need to go back and check --- dll files use assembly-like code)

and I never knew the 'attr' was for enabled/handling having a space within the 'name' of the Element/xml tag, I just find it so much more useful, sighs.


P.S.

on a totally separate note:

I tried using this common style of syntax (on C++ at least, I think, and probably Java and Python, but not sure) that some people use as it can be easier for new people to understand it, but quest isn't programmed to handle it (using 'if' for the example):

if (XXX)
{
  // XXX
}
else if (XXX)
{
  // XXX
}
else
  // XXX
}

if I understand the error (did NOT do a very thorough job of testing), it needs the beginning curly brace on the same line as the 'if/else-if/else'


I do not know where it is, but it will not be a .aslx file. I would presume in the C# code, but I have yet to come across any XML handlers.


is the 'C#' code, the source code, or something else? where do I find the 'C#' code (example of a file), if you know of any locations of it ???


ah, okay, so the 'attr' usage is a 'xml' handler... at least now I know what I'm trying to find/look for, lol...


K.V.

I don't exactly understand what you're trying to do. (This is because you know what you're doing programming-wise, and I'm only just learning.)

You want to do this:

<attr name="NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE" type="TYPE_OF_ATTRIBUTE">
  CONTENT (depends on Attribute Type): <value><value>, <item><key></key><value></value></item>, <item key="XXX">SCRIPTING</item>
</attr>

As opposed to this:

<NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE type="TYPE_OF_ATTRIBUTE">
  CONTENT (depends on Attribute Type): <value><value>, <item><key></key><value></value></item>, <item key="XXX">SCRIPTING</item>
</NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE>

The only difference I see between the two is the attr tag VS the NAME_OF_LIST_OR_DICTIONARY_ATTRIBUTE tag.


Anyway, in WorldModel\WorldModel\GameLoader\ElementLoaders.cs, starting at line 420, I found this, which I believe may be what you're looking for:

public override object Load(XmlReader reader, ref Element current)
            {
                string attribute = reader.Name;
                if (attribute == "attr")
                {
                    attribute = reader.GetAttribute("name");
                }
                string type = reader.GetAttribute("type");

                WorldModel.AddAttributeName(attribute);

                if (type == null)
                {
                    string currentElementType = current.Fields.GetString("type");
                    if (string.IsNullOrEmpty(currentElementType))
                    {
                        // the type property is the object type, so is not set for other element types.
                        currentElementType = current.Fields.GetString("elementtype");
                    }
                    type = GameLoader.m_implicitTypes.Get(currentElementType, attribute);
                }

                // map old to new type names if necessary (but not in included library files)
                if (type != null && WorldModel.Version <= WorldModelVersion.v530 && !current.MetaFields[MetaFieldDefinitions.Library] && s_legacyTypeMappings.ContainsKey(type))
                {
                    type = s_legacyTypeMappings[type];
                }

                if (type != null && GameLoader.ExtendedAttributeLoaders.ContainsKey(type))
                {
                    GameLoader.ExtendedAttributeLoaders[type].Load(reader, current);
                }
                else
                {
                    string value;

                    try
                    {
                        value = GameLoader.GetTemplateContents(reader);
                    }
                    catch (XmlException)
                    {
                        RaiseError(string.Format("Error loading XML data for '{0}.{1}' - ensure that it contains no nested XML", current.Name, attribute));
                        return null;
                    }

                    if (type == null)
                    {
                        if (value.Length > 0)
                        {
                            type = "string";
                        }
                        else
                        {
                            type = "boolean";
                        }
                    }

                    if (GameLoader.AttributeLoaders.ContainsKey(type))
                    {
                        GameLoader.AttributeLoaders[type].Load(current, attribute, value);
                    }
                    else
                    {
                        Element del;
                        if (WorldModel.Elements.TryGetValue(ElementType.Delegate, type, out del))
                        {
                            Element proc = WorldModel.GetElementFactory(ElementType.Delegate).Create();
                            proc.MetaFields[MetaFieldDefinitions.DelegateImplementation] = true;
                            proc.Fields.LazyFields.AddScript(FieldDefinitions.Script.Property, value);
                            current.Fields.Set(attribute, new DelegateImplementation(type, del, proc));
                        }
                        else
                        {
                            RaiseError(string.Format("Unrecognised attribute type '{0}' in '{1}.{2}'", type, current.Name, attribute));
                        }
                    }
                }
                return null;
            }

K.V.

I also found these in WorldModel\WorldModel\Functions\ExpressionOwner.cs, starting at line 381:

        public object DictionaryItem(/* IDictionary */ object obj, string key)
        {
            IDictionary dictionary = GetParameter<IDictionary>(obj, "DictionaryItem", "dictionary");
            return dictionary[key];
        }

        public string StringDictionaryItem(/* IDictionary */ object obj, string key)
        {
            IDictionary dictionary = GetParameter<IDictionary>(obj, "StringDictionaryItem", "dictionary");
            return dictionary[key] as string;
        }

        public Element ObjectDictionaryItem(/* IDictionary */ object obj, string key)
        {
            IDictionary dictionary = GetParameter<IDictionary>(obj, "ObjectDictionaryItem", "dictionary");
            return dictionary[key] as Element;
        }

        public IScript ScriptDictionaryItem(/* IDictionary */ object obj, string key)
        {
            IDictionary dictionary = GetParameter<IDictionary>(obj, "ScriptDictionaryItem", "dictionary");
            return dictionary[key] as IScript;
        }

The first one, ElementLoaders.cs, is the right sort of thing, but I see nothing there that would cause this behaviour. That said, it does pass off the work to AttributeLoaders and it may be in them. Who knows where they are?

The stuff in ExpressionOwner.cs looks like the hard-coded functions used in Quest code.


Edit: This post isn't exactly wrong, but it isn't where the problem is. The loaders in AttributeLoaders.cs (analysed here) are the ones that work. See my next reply for a look into ExtendedAttributeLoaders.cs, which will parse <attr name="foo"> as an attribute named "attr", ignoring the name= bit.

In WorldModel/WorldModel/GameLoader/AttributeLoaders.cs there are a load of classes derived from AttributeLoaderBase. Each one has a Load function, which should parse the contents of a particular attribute type. For example:

        private class SimpleStringListLoader : AttributeLoaderBase
        {
            public override string AppliesTo
            {
                get { return "simplestringlist"; }
            }

            public override void Load(Element element, string attribute, string value)
            {
                string[] values = GetValues(value);
                element.Fields.Set(attribute, new QuestList<string>(values));
            }

            protected string[] GetValues(string value)
            {
                string[] values;
                if (value.IndexOf("\n", StringComparison.Ordinal) >= 0)
                {
                    values = Utility.SplitIntoLines(value).ToArray();
                }
                else
                {
                    values = Utility.ListSplit(value);
                }
                return values;
            }
        }

the AppliesTo method returns "simplestringlist"; so this class's Load() method will be called whenever Quest sees an <attr name="whatever" type="simplestringlist"> or <whatever type="simplestringlist">

I don't really know C#; but I can't see anything that should make the <attr> work for only some of these types, unless these Load methods treat it oddly.


Ouch ... I see the problem.

First it checks for a loader in ExtendedAttributeLoaders; then it looks in AttributeLoaders.

The latter had a method: Load(Element element, String attribute, String value). This parses value (converting it to a string, list, int, or whatever) and then pretty much calls the C# function that lies behind set(element, attribute, value).

The former, on the other hand, has a method Load(XmlReader reader, Element current). It can use the methods of the XmlReader object to access nested XML tags within the value; for example the <item><key></key><value></value></item> syntax mentioned above. but some of these loaders use reader.Name to determine the name of the attribute to add to the object specified by element.
In this case, reader.Name will be "attr".

So, in the file WorldModel/WorldModel/GameLoader/ExtendedAttributeLoaders.cs, it has:

       private class ScriptDictionaryLoader : ExtendedAttributeLoaderBase, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "scriptdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                string currentXmlElementName = reader.Name;
                Dictionary<string, string> result = LoadScriptDictionary(reader, current, currentXmlElementName);
                current.Fields.LazyFields.AddScriptDictionary(currentXmlElementName, result);
            }

but it should should be:

       private class ScriptDictionaryLoader : ExtendedAttributeLoaderBase, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "scriptdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                string currentXmlElementName = reader.Name;
                string attributeName = reader.GetAttribute("name");
                if (attributeName == null) {
                  attributeName = currentXmlElementName;
                }
                Dictionary<string, string> result = LoadScriptDictionary(reader, current, currentXmlElementName);
                current.Fields.LazyFields.AddScriptDictionary(attributeName, result);
            }

and the same for the other classes in that file.


Note: Two ways to do the same thing:

    string attributeName = reader.GetAttribute("name");
    if (attributeName == null) {
      attributeName = currentXmlElementName;
    }

or

    string attributeName = currentXmlElementName;
    if (attributeName == "attr") {
      attributeName = reader.GetAttribute("name");
    }

Both will cause annoying and hard to diagnose bugs in some crcumstances.
The former has a problem with <someattribute name="somethingelse">
The latter will break on <attr name="attr"> (which from a cursory glance, I think might get turned into just <attr> in a saved game)

I figured that the former will only happen if you're editing the XML. The latter could happen in a save game if the game allows you to name an attribute "attr". So I went for the one that's least likely to occur in some unexpecting user's game.

However, as far as I can tell, Quest's existing code uses the latter.


K.V.

That makes sense, mrangel.

So, it's probably best to leave it be?


thanks to all of you for the work with trying to help me, I can barely understand the coding, but had/having a hard time in understand what its doing in terms of its design structure (as it's ambigious/abstract for each part/section of its code sections), jumping/branching around the encapsulation/modules, and etc, so your posts were very helpful, wish I could have figured/found/understood all that stuff out that you guys were able to, sighs, I still got a long ways to go with learning to code/program, sighs) ... I'll see if I can figure out how to deal with the bugs (presumably having to trace down all of the interconnections with the part of the code mrangel, pixie, and KV found)...


There is a perhaps related issue that if you call an attribute "object" (and certain other names too), when the game is saved, it saves as:

<object>Some text</object>

When it then loads the game, it treats that as an object, rather than as an attribute called object. Is there a way we can sort all these issues (and still be backwards compatible?)


(Edit: This was written in response to KV's post; seems some other replies arrived while I was typing)
I see no reason not to change it.

It's just that in the course of looking through this, I've seen what looks like an existing problem. The save functions will generate <attributename type="type"> elements for any attribute whose name is purely alphabetical. Even if the attribute name is "attr", "object", "exit", or something else that is an actual XML element. I've not tested it yet, but I'd be very surprised if that didn't end up with those attributes disappearing when you load a saved game.

Has anyone tried this? If it behaves sanely,I'd be very interested to find out what I missed.


Pixie:
I've been looking into that.
I've suggested code above to make <attr name="whatever"> syntax work for all types, including the ones that contain XML (dictionaries etc).

I would then suggest modifying WorldModel/WorldModel/GameLoader/FieldSaver.cs
The code:

        private abstract class FieldSaverBase : IFieldSaver
        {
            public abstract Type AppliesTo { get; }
            public abstract void Save(GameXmlWriter writer, Element element, string attribute, object value);

            private static Regex s_regex = new Regex("^[A-Za-z0-9]*$");

            protected void WriteAttribute(GameXmlWriter writer, Element element, string attribute, string type, string value)
            {
                if (!s_regex.IsMatch(attribute))
                {
                    // For attribute names with spaces or accented characters, we output
                    //      <attr name="my attribute" ... />
                    writer.WriteStartElement("attr");
                    writer.WriteAttributeString("name", attribute);
                }
                else
                {
                    // For attribute names without spaces, we output
                    //      <myattribute ... />
                    writer.WriteStartElement(attribute);
                }

Two possible suggestions.
1: Always use <attr> (but keep recognising the other style when loading, for legacy compatibility)

        private abstract class FieldSaverBase : IFieldSaver
        {
            public abstract Type AppliesTo { get; }
            public abstract void Save(GameXmlWriter writer, Element element, string attribute, object value);

            protected void WriteAttribute(GameXmlWriter writer, Element element, string attribute, string type, string value)
            {
                // Now we always output
                //      <attr name="my attribute" ... />
                writer.WriteStartElement("attr");
                writer.WriteAttributeString("name", attribute);

2: specifically use <attr> for names that have specific meaning

        private abstract class FieldSaverBase : IFieldSaver
        {
            public abstract Type AppliesTo { get; }
            public abstract void Save(GameXmlWriter writer, Element element, string attribute, object value);

            // regex now matches attribute names that should be saved in <attr> form
            private static Regex s_regex = new Regex("[^A-Za-z0-9]|attr|object|exit|command");

            protected void WriteAttribute(GameXmlWriter writer, Element element, string attribute, string type, string value)
            {
                // Note that I removed the ! from this expression
                if (s_regex.IsMatch(attribute))
                {
                    // For attribute names with spaces or accented characters, we output
                    //      <attr name="my attribute" ... />
                    writer.WriteStartElement("attr");
                    writer.WriteAttributeString("name", attribute);
                }
                else
                {
                    // For attribute names without spaces, we output
                    //      <myattribute ... />
                    writer.WriteStartElement(attribute);
                }

(I know my regex is heavy handed. But I cannot see a problem with using <attr name="whatever"> in cases that aren't strictly necessary)


K.V.

I have Quest open in Visual Studio.

Somebody tell me what to change, and I will test things out.


K.V.

While we're at it, maybe we could add things like:

int = 0
int++

myString = "Hello"
myString += ", world!"

If not, this is not a big deal. It would be quite a luxury, though; wouldn't it?


@KV:
In WorldModel/WorldModel/GameLoader/ExtendedAttributeLoaders.cs
there are several classes that start off looking like:

  private class ScriptDictionaryLoader : ExtendedAttributeLoaderBase, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "scriptdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                string currentXmlElementName = reader.Name;
                Dictionary<string, string> result = LoadScriptDictionary(reader, current, currentXmlElementName);
                current.Fields.LazyFields.AddScriptDictionary(currentXmlElementName, result);
            }

to fix the issue mentioned at the top of this thread, you will want to change those to:

  private class ScriptDictionaryLoader : ExtendedAttributeLoaderBase, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "scriptdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                string currentXmlElementName = reader.Name;
                string attributeName = reader.GetAttribute("name");
                if ((currentXmlElementName != "attr") || (attributeName == null)) {
                  attributeName = currentXmlElementName;
                }
                Dictionary<string, string> result = LoadScriptDictionary(reader, current, currentXmlElementName);
                current.Fields.LazyFields.AddScriptDictionary(attributeName, result);
            }

(each of them has a different line where it assigns something to result, which is the line that doesn't need changing in this example)

For fixing attributes named "attr", "object", "exit" etc (has anyone actually tested that? I only said that looking at the code, I would expect it to mess up the XML in a saved game) is a more complex issue, so we can deal with that as stage 2.


K.V.

I changed the bit in ExtendedAttributeLoaders.cs to this:

       private class ScriptDictionaryLoader : ExtendedAttributeLoaderBase, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "scriptdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                string currentXmlElementName = reader.Name;
                string attributeName = reader.GetAttribute("name");
                if (attributeName == null) {
                  attributeName = currentXmlElementName;
                }
                Dictionary<string, string> result = LoadScriptDictionary(reader, current, currentXmlElementName);
                current.Fields.LazyFields.AddScriptDictionary(attributeName, result);
            }

Then, I changed the bit in FieldSaver.cs to this:

private abstract class FieldSaverBase : IFieldSaver
        {
            public abstract Type AppliesTo { get; }
            public abstract void Save(GameXmlWriter writer, Element element, string attribute, object value);

            protected void WriteAttribute(GameXmlWriter writer, Element element, string attribute, string type, string value)
            {
                // Now we always output
                //      <attr name="my attribute" ... />
                writer.WriteStartElement("attr");
                writer.WriteAttributeString("name", attribute);

This code works now (and Quest does not alter the code in code view after reloading the game file):

<!--Saved by Quest 5.7.6634.18786-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Dict Attr Tester">
    <attr name="gameid">deb81fec-d0ec-41d7-8335-c9395e60b7ba</attr>
    <attr name="version">1.0</attr>
    <attr name="firstpublished">2018</attr>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <attr name="enter" type="script">
      invoke(ScriptDictionaryItem(player.myDict,"one"))
    </attr>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <attr name="myDict" type="scriptdictionary">
        <item key="one">msg("One")</item>
      </attr>
    </object>
  </object>
</asl>

K.V.

Well, the game worked, but I got this when I ran the tests in VS:

Test Failed - Run Walkthrough

Message: Assert.IsTrue failed.Initialisation failed.

I tried to run the tests a second time, because I've seen a test fail once, but pass 100 times afterwards.

This test fails.

I'm going to try one of the alternative codes mrangel posted now.


K.V.

Ah.

mrangel posted while I was writing a post, and I missed it.

Trying the last thing he posted now.

(Undoing the change to FieldSaver.cs for now, too.)


K.V.

Okay...

I undid the previous changes.

Then, I only changed the existing code in ExtendedAttributeLoaders.cs (starting at line 64) to this:

  private class ScriptDictionaryLoader : ExtendedAttributeLoaderBase, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "scriptdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                string currentXmlElementName = reader.Name;
                string attributeName = reader.GetAttribute("name");
                if ((currentXmlElementName != "attr") || (attributeName == null)) {
                  attributeName = currentXmlElementName;
                }
                Dictionary<string, string> result = LoadScriptDictionary(reader, current, currentXmlElementName);
                current.Fields.LazyFields.AddScriptDictionary(attributeName, result);
            }

Now, all the tests pass, and the example game still works.

The only thing:

Quest changes the code when reloading the game now.

...but you can still input the code as it is in the first version of this example game I posted.

It will just change it to this:

<!--Saved by Quest 5.7.6634.20080-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Dict Attr Tester">
    <gameid>deb81fec-d0ec-41d7-8335-c9395e60b7ba</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <enter type="script">
      invoke (ScriptDictionaryItem(player.myDict,"one"))
    </enter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <myDict type="scriptdictionary">
        <item key="one">
          msg ("One")
        </item>
      </myDict>
    </object>
  </object>
</asl>

The change to FieldSaver.cs was what kept Quest from changing the code around on reload.

I'm going to try mrangel's alternate method to see if that will pass the tests.


there are several classes that start off looking like:

I don't see any more, but I'm still looking.


For fixing attributes named "attr", "object", "exit" etc (has anyone actually tested that?

I didn't test it, but Pixie has mentioned it a few times, so I take that as gospel.


K.V.

Now, I have changed the bit in FieldSaver.cs (starting at line 100) to this:

private abstract class FieldSaverBase : IFieldSaver
        {
            public abstract Type AppliesTo { get; }
            public abstract void Save(GameXmlWriter writer, Element element, string attribute, object value);

            // regex now matches attribute names that should be saved in <attr> form
            private static Regex s_regex = new Regex("[^A-Za-z0-9]|attr|object|exit|command");

            protected void WriteAttribute(GameXmlWriter writer, Element element, string attribute, string type, string value)
            {
                // Note that I removed the ! from this expression
                if (s_regex.IsMatch(attribute))
                {
                    // For attribute names with spaces or accented characters, we output
                    //      <attr name="my attribute" ... />
                    writer.WriteStartElement("attr");
                    writer.WriteAttributeString("name", attribute);
                }
                else
                {
                    // For attribute names without spaces, we output
                    //      <myattribute ... />
                    writer.WriteStartElement(attribute);
                }

This passes all the tests in VS.

AND, it doesn't delete (or change) the attribute I named object.

It doesn't do the string dictionary correctly, but this is because I need to find the other bits that need changing in ExtendedAttributeLoaders.cs . (The string dictionary loader code is not the same as the script dictionary loader.)


This is what it changes my example game's code to after reloading (note: the string dictionary was once named "stringDict"):

<!--Saved by Quest 5.7.6634.20567-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Dict Attr Tester">
    <gameid>deb81fec-d0ec-41d7-8335-c9395e60b7ba</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <beforeenter type="script">
      msg (player.object)
    </beforeenter>
    <enter type="script">
      invoke (ScriptDictionaryItem(player.myDict,"one"))
    </enter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <attr type="stringdictionary">
        <item>
          <key>first</key>
          <value>This is first.</value>
        </item>
      </attr>
      <myDict type="scriptdictionary">
        <item key="one">
          msg ("One")
        </item>
      </myDict>
      <attr name="object">TEST</attr>
    </object>
  </object>
</asl>

FILLER


K.V.

I'm thinking these are the three classes that need to be fixed:

private class StringDictionaryLoader : DictionaryLoaderBase<string>, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "stringdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                var result = LoadDictionary(reader, current.Name);
                current.Fields.Set(reader.Name, new QuestDictionary<string>(result));
            }
private class ObjectDictionaryLoader : DictionaryLoaderBase<string>, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "objectdictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                var result = LoadDictionary(reader, current.Name);
                current.Fields.LazyFields.AddObjectDictionary(reader.Name, result);
            }
private class DictionaryLoader : DictionaryLoaderBase<object>, IValueLoader
        {
            public override string AppliesTo
            {
                get { return "dictionary"; }
            }

            public override void Load(XmlReader reader, Element current)
            {
                var result = LoadDictionary(reader, current.Name);
                current.Fields.Set(reader.Name, new QuestDictionary<object>(result));
            }

wow, you guys are awesome! thanks for all the work and help you've done with this matter. When I got the time (been a bit busy), I'll study, follow, and learn closely the part of quest code that you guys helped with all of that stuff, as I want to understand how the quest code is working a bit, along with how you fixed it, and etc.


@ KV:

My interest was in preserving and having/using the 'attr' (for Lists and Dictionaries), as that was what my topic was about doing.

so if it is being altered by quest back away to the 'non-attr' syntax, then that defeats the whole point, lol


K.V.

That's what I was thinking, HK.

The first bit of code mrangel posted for FieldSaver.cs did just that.

It even saved the enter script I set up in the GUI as:

<attr name="enter" type="script">
  // Scripting
</attr>

...but that code made the walkthrough test in Visual Studio fail. (I may have just needed to alter some other code, to make it all work together.)


..but that code made the walkthrough test in Visual Studio fail.

I expect that it's failing because the test explicitly expects it to save some attributes in the "non-attr" form; if games actually run fine, then it might mean that the test is checking for something it doesn't need to.

I'm thinking these are the three classes that need to be fixed:

From a quick skim-read through ExtendedAttributeLoaders, it looks like the classes that need to be changed are:

  • ScriptDictionaryLoader
  • StringListLoader
  • ListLoader
  • StringDictionaryLoader
  • ObjectDictionaryLoader
  • DictionaryLoader

The lines that need to be changed are:

  • current.Fields.LazyFields.Add(Type)(currentXmlElementName,(some data))
  • current.Fields.LazyFields.Add(Type)(reader.Name,(some data))
  • current.Fields.Set(reader.Name,(some data))

In each case replacing either currentXmlElementName or reader.Name with a new variable, and adding the code to deduce that variable before the XML is parsed.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums