Multiplayer Interactive-Fiction Game-Design Blog


Objects – Superclasses and subclasses



Download 8.87 Mb.
Page104/151
Date02.02.2017
Size8.87 Mb.
#15199
1   ...   100   101   102   103   104   105   106   107   ...   151

Objects – Superclasses and subclasses


How to create superclasses and subclasses.
Objects can inherit methods and properties from other classes/objects.

As stated previously, this is useful for the example Parrot object

that can inherit methods and properties from the soon-to-be-created

"Bird" class, which in turn could inherit from an "Animal" class, etc.


Each class provides a way of providing common methods and

properties for a group of like individuals.

A "superclass" is a class (or object) with methods or properties

that are inherited by other classes. A "subclass" is a class

that inherits from a superclass.


Creating a superclass

To create the "Bird" superclass:







  1. Create an object/class called "Bird", using the

    same general actions as those used to create "Parrot".





  2. Unlike Parrot, the "Automatically create as an object",

    found under "Super-classes" in

    the "Modify object" page, should not be checked.

    (It could be checked, but then you'd end up with both a Parrot

    object and a Bird object.)





  3. As before, add the "FlightSpeed" public property to

    the Bird class, and type in an initial value, like 10.





  4. Add the "Fly" public method to the Bird class and provide

    that outputs a Trace("This is Fly from the bird class") so you

    can tell that it was called.



That's it; you've created a bird class.

(Of course, this is a very simple example so you can understand

the process.)



Using a superclass
To get the Parrot object to use the Bird class:





  1. Switch to the "Modify object" page for the Parrot

    object, and press the "Super-classes" tab if

    it isn't already visible.



  2. Press the "Add class" button.




  3. The "Add class(es)" page will appear. It lists all of the

    classes in the project.




  4. Check the "Bird" class.





  5. Return to the "Modify object" page for the

    Parrot object. You'll see that "Bird" is now listed as

    one of the superclasses.


  6. If you compiled now though, the Parrot class would be unchanged

    because it already supports the "FlightSpeed" property and "Fly"

    method. Because of inheritance rules, if a sub-class (aka: the

    Parrot object) supports a method or property from a super-class (aka:

    the Bird class) then the sub-class's properties or methods

    have priority.

    Therefore, you need to remove both the "FlightSpeed" property

    and "Fly" method from the Parrot object (not the Bird class).


  7. Switch to the Properties tab and change the

    property drop-down for "FlightSpeed" to "blank", effectively

    deleting the property.





  8. Switch to the Methods tab, hold

    the control key down, and click on

    the "Fly" method button. This will delete the

    method.


  9. Since Parrot no longer supports the properties and methods

    from the Bird class, they will be inherited directly.


  10. To test your changes, compile and

    run the Test compiled code. The FlightSpeed

    property will be the value initialized in the Bird class,

    and the Fly method will call the code from the Bird class.





Why use superclass?

So what? That didn't really do much. Now that you have a

Bird class that defined flying, here's how you can use it:





  • Override a property's inheritance - If you add the

    "FlightSpeed" property back into the Parrot class but use

    a new initial value, you can make Parrots fly slower or

    faster than the "typical" bird.







  • Override a method's inheritance - If you add the

    "Fly" method back into the Parrot class with different code

    you can make parrot's fly differentlu than "typical" birds.





  • Other subclasses - Since most types of birds (swans,

    ducks, hawks, etc.) are really just small variations on the

    basic "bird" concept, you can quickly create many different

    types of birds (sub-classes) based on the "Bird" super-class.

    Where the type of bird (swan, duck, etc.) varies from the

    super-class "Bird", you provide new properties and/or methods.







  • Multiple inheritance - As stated in the last

    tutorial, you can have the Parrot object inherit not only

    from the Bird class, but also from a "TalkingCreature" class.

    Parrots, humans, aliens, and even robots would be

    members of the "TalkingCreature" class, sharing properties

    and code specific to creature that talk.






I won't lead you through all these examples step-by-step

because they're all variations on a theme.


Superclass order
If an object is a sub-class to two or more super-classes

you may need to prioritize the super-classes so that any

overrides (for properties or methods) are correct. Such

situations will arise if two or more superclasses support

the same properties or methods.

The "Super-classes" tab in the "Modify object"

lets you control the priority of all the object's

superclasses.




Method inheritance
Earlier I said that if a class (the Parrot object) provides

code for a method (the "Fly" method),

any code from the superclass (the "Fly" method in the Bird class)

will be ignored.


I lied. This isn't always the case.


Some method definitions allow the methods to coexist.

You can have a call

to a method first call the method in the lowest sub-class (the

Parrot), then the next sub-class (the Bird class), and the

next one (the Animal class), etc. You can reverse the order

and call the methods from the highest super-class first, down

to the lowest sub-class. You can also allow this chain to

be stopped based on the returns of the methods. This creates

four possibilities, in addition to the "just override inheritance"

that's the default.

You might want to do this to accomplish:




Cumulative effects - If you wanted to allow the

bird with an egg inside it to fly slower, because it weighs more,

you could temporarily decrease the "FlightSpeed" property and

then try and remember to increase it when the egg was laid.


Alternatively, you could create a "FlightSpeedGet" method that

just did the following:

return (parameter[0] = FlightSpeed);




This would return the bird's flying speed, and would be

the "official" way of getting the value; you could even make

the property private.

The variable, parameter, is automatically defined to be the

list of parameters passed into the method. Since the

FlightSpeedGet() call takes no parameters, the list will

initially be empty. Setting paramter[0], however, changes this.

Normally, this would have no effect... however:


You could also create a super-class called "Pregnant". It would

support the FlightSpeedGet() method too, but its code would be:

return (parameter[0] *= 0.75);




If you had the FlightSpeedGet() method call the super-class

first, parameter[0] would be set to the original flight

speed. Once that returned, the next class down (Pregnant) would

be called; this would retrieve the value "stashed away" in

parameter[0], decrease it to account for the egg's extra weight,

and return that.


Thus, to make a pregnant Parrot you'd include the Pregnant

class as a super-class. You could also add and remove the

pregnancy later using layers (discussed later).


This idea could be extended further by making an "Unhealthy"

super-class that likewise reduces flying speed. If a Parrot

is both Pregnant and Unhealthy it will fly even slower.



Sometimes capture, sometimes pass it on - Alternatively,

you could tell the method definition to call all the methods

from sub-to-super-class or super-to-sub-class, repeating until

one of them comes up knows how to deal with the situation.


Perhaps you might need a public method, LikeFood (FoodName), that returns

true if the object (aka: Parrot or Bird) likes a food item (passed

in as the parameter "FoodName") or false if it doesn't.


The generic bird would return true for (FoodName == "birdseed") ||

(FoodName == "peanut"), but

false for (FoodName == "flower").


return (FoodName == "birdseed") || (FoodName == "peanut");




Some parrots like flowers, in addition to to birdseed. You could

provide have the LikeFood() method override, cut-and-paste the

code from the Bird's LikeFood() method, and then modify it.

Or, you could have specify (in the method definition) that it

should call from the highest priority methods (the Parrot's)

to the lowest priority methods (the Bird's), trying

LikeFood() for each one. If any returns a value which is not

undefined, then that return is used. Otherwise, the next in

line is called.

The Parrot's LikeFood() method would then be:


if (FoodName == "flower")


&tab;return true;


else

&tab;return undefined; // pass it on


This makes for a very elegant solution, especially if new

foods are added later on.

Both the "cumulative effects" and "sometimes capture, sometimes

pass on" approaches to methods are supported by

MIFL. To have a method definition use either of these:



  1. Switch to the "Misc." tab in the "Modify

    method definition" page for the method you wish to

    change.




  2. Change the "Overrides" setting to the method

    you wish to use. The default is "Call only the highest

    priority method", but the other four possibilities are

    also listed.



  3. Make sure to document this behavior so that

    anyone using that method definition will know what to expect.





Recommended properties and methods
Realistically, the flying speed for every bird is different,

and while a few birds will fly at the FlightSpeed (property)

as initialized by the Bird superclass, most birds will fly

at different speeds.


To make sure that anyone creating a new type of bird will

override the default FlightSpeed property for the bird, or

consciously decide not to override it, you can can add

a setting to the "Bird" class so that if any other class

is derived from it (such as a "Robin"), an entry for

the FlightSpeed property will automaically be added to the

Robin class's list of public properties.


To do this:





  1. Switch to the "Misc." tab in the "Modify object"

    page for the "Bird" class.



  2. Press the "Recommend properties and methods for

    sub-classes" button.


  3. The "Recommended" page will appear, displaying the properties

    and methods that are automatically added to any sub-classes

    created from the Bird class.



  4. Check the "FlightSpeed" property.



Once the "FlightSpeed" property is checked, anytime the Bird

class is included as a super-class of another object (or class),

the "FlightSpeed" property will automatically be added to

the object's list of public properties.

    1. Objects – Layers


Describes what layers are and how to use them.
MIFL includes an object concept called "layers".

They allow an program to add new super-classes to an object

at run-time. They also allow new methods and property get/set

calls to be added at run-time.


The primary use for layers is to allow objects to dynamically

change their beahaviors (methods) and properties in an

easy-to-program way. As an example from the last tutorial,

layers could be used to add a "Pregnant" or "Unhealthy" superclass

to a "Parrot" object. The layer allows all the methods

associated with being Pregnant or Unhealthy to be added

at run-time, and removed when the state no longer applied.


To see how this works, first create a superclass that can be layered.







  1. Create an "Unhealthy" super-class.





  2. Add the public method, "Fly", to the Unhealthy super-class.

    The code should be different than before:








Trace ("An unhealthy bird can't fly.");


return false;




That's all you need to test out the concept. You'll know

if the unhealthy superclass has been added to the Parrot

object because calling Fly() will output different text.

To test the Unhealthy superclass:







  1. Compile the project and

    then run the Test compile code menu item.





  2. Type "Parrot.Fly()" and press "Run this".




  3. From the debug trace you'll see that the Fly() code for the Parrot

    comes from the Bird class. The parrot is still healthy.


  4. To add the Unhealthy class to the parrot object,

    type "Parrot.LayerAdd ("SickLayer", "Unhealthy", 1);" and

    press "Run this".




  5. The "SickLayer" parameter is a name that you given to the layer

    so you can later idenfity which layer it is. (After all, you might

    have several layers.) The "Unhealthy" parameter is the name of the

    class. And 1 is the priority of the class; higher numbers have

    higher priority over other classes. The class used to create

    an object always has priorty 0, and is usually the lowest

    priority.

  6. Now, type "Parrot.Fly()" and press "Run this".




  7. The Fly() code from the Unhealthy class will be run instead.

    The debug trace will indicate that the parrot is too sick to fly.


  8. You can also see the layer by using the right column

    of the test display. Click on "Objects...",

    then "Parrot", and finally, hover the mouse

    over the "Layers" entry.



  9. To remove the layer,

    type "Parrot.LayerRemove (0);" and

    press "Run this".




  10. The 0 parameter is the index into the layer. If you used 1

    instead, the "Parrot" class would be removed from the object,

    making it an unhealthy nothing. Under normal circumstances

    you'd look through all the layers in an object to find

    the one named "SickLayer" and then delete that one. This

    is just a tutorial though.


There are quite a few layer methods you can call. Some

of them are:





  • LayerAdd - Adds a new layer to an object.





  • LayerGet - Gets information about a layer





  • LayerNumber - Returns the number of layers in an object.





  • LayerRemove - Removes a layer from an object.




Adding individual methods
You can also add individual methods to a layer (although

adding classes (aka: groups of methods) is recommended).

To add an individual method:



  1. In the "Test compiled code" page,

    type "Parrot.LayerMethodAdd (0, "Speak", Trace)" and

    then press "Run this".




  2. The 0 parameter causes the method to be added to the layer

    at index location 0. "Speak" is the name that the new method

    will use. And, Trace, is the function (or method) to call when

    "Speak" is called.

    At the moment, you cannot compile code on-the-fly, so the

    only code you can add is that which has already been compiled,

    either in a function, this object, or another. (I can add

    the ability to code on-the-fly, if necessary.)


  3. Because "Speak" is not a compiled token, you can't

    just type "Parrot.Speak ()". However, you can call

    the method using, "Parrot.MethodCall ("Speak", "Polly wants

    a cracker." and pressing "Run this".


  4. The "Speak" parameter is the name of the method to call; it

    could also be "Fly", or other methods supported by the Parrot.

    "Polly wants a cracker." is the parameter passed into the "Speak"

    method, which just ends up calling Trace().

    Therefore, you should see "Polly wants a cracker." appear

    on your debug trace.



Layers and properties
When you include a super-class in your object, it automatically

inherits alls the properties from the superclass.

This is not the case for layers. If you add

a layer with a new class, the method definitions are incorporated

into the object, but properties are not.

MIFL specifically does not add properties when a layer is added

since doing so might unpredictably (for the programmer) cause

hundreds of properties to suddenly change.


Of course, there's an easy work around for this: Just set the

properties immediately before or after the layer. Or, provide

a function in the layered class that initialzes any necessary

properties.

One aspect of the properties is inherited when you layer on

a super-class though. Properties can include code that is called

whenever the property is accessed, through a get() or set().

Some properties need to trap access calls so they can verify the

value they're being changed to is acceptable.


If a layered super-class includes code for get() and set() of

its properties, this get() and set() code will be incorporated

into the object while the layer exists.


Furthermore, just as there's a way to add individual methods

to a layer, there is also a way of adding get() and set() code

for properties to a layer. This won't be discussed in the

tutorial though.


    1. Objects – Containership


Tutorial about how one object can contain another.
MIFL objects understand containership. They can contain other

objects and be contained by an object. This is an extremely

useful tool for interactive fiction, which has all sorts

of containers, like rooms containing objects, characters

containing inventory objects, and backpacks containing

objects.

To have an object initially created within another object:



  1. First create an object that can be a container. Using

    everything you've learned so far, create

    a "Birdcage" object. It doesn't need any properties

    or methods. Make sure it has the "Automatically

    create as an object" option checked.





  2. Modify the Parrot object so it starts out contained within

    the birdcage. Bring up the Parrot's "Modify object" page

    and switch to the "Super-classes" tab.





  3. Underneath the "Automatically create as an object" checkbox

    is a drop-down listbox. Open it up and select "Birdcage" from

    the listbox.







  4. Compile the code and go to Test

    compiled code.





  5. You can use the right column to see the containership;

    click on "Objects...", then "Birdcage",

    and hover the mouse over the "Contains" item.

    A popup will appear showing that the birdcage contains

    the parrot.





  6. If you switch to the Parrot object you'll see

    that it is contained by the birdcage.



That's all you need to do.


You can change containership at run-time using the following

methods:





  • ContainedInGet - Returns the object that contains

    this object.







  • ContainedInSet - Moves the object into another

    object.






  • ContainsEnum - Returns a list of objects

    that this object contains.






    1. Objects – Timers


Tutorial about the use of timers.
In MIFL, all timer events are associated with objects.

This means that to enumerate all the timers, you need to enumerate each

object and discover what times it supports. It also

causes times to be deleted when the object is deleted, or

saved when the object is saved.

Creating a timer is easy:





  1. Switch to the Test compiled code window.





  2. Type "Parrot.TimerAdd ("SpeakTimer", true,

    1.0, Trace, "Polly wants a cracker.") and

    press "Run this".




  3. The "SpeakTimer" is a name you can use to identify the

    timer. True indicates that the timer keeps repeating; if you

    passed in false it would only call itself once and then stop.

    1.0 is the number of seconds before the timer goes off.

    Trace is the function (or method) to call, and

    "Polly wants a cracker." is the parameter to pass into the

    function (or method).

    In other words, this timer causes the parrot to speak,

    "Polly wants a cracker." over and over again.

  4. To see the effects, press the "Refresh" button

    in the "Debug trace" section.



  5. You can also see the timer by clicking, "Timers (active)..." in

    the right-hand table.




There are three ways to stop the timer:





  • Run "Parrot.TimerRemove ("SpeakTimer")". This

    deletes the timer.



  • Run "Parrot.TimerSuspendedSet (0.0)". This

    suspends all of the timers in the parrot object.

    You can reactivate the timers by calling this function and

    passing false in as the parameter.



  • Delete the parrot by running "delete Parrot".





That's the basics. Here's a complete list of timer methods:







  • TimerAdd - Adds a new timer to the object.





  • TimerEnum - Enumerates all the timers in an object.





  • TimerQuery - Returns information about a timer.





  • TimerRemove - Deletes a timer from an object.





  • TimerSuspendedGet - Returns TRUE if the object's

    timers are suspended.







  • TimerSuspendedSet - Suspends or resumes an

    object's timers.







    1. String tables and resources


Tutorial about the use of timers.
MIFL includes two special entities, string tables and resources.

The are both extremely useful for writing an application.



String table
The "String table" is a list of strings with

a name attached to each. For example: One entry in the string

table might be "Hello world!" with the name, SHelloWorld (the S

prefix indicating it's a string).


When you have entered a string into the string table, you

can refer to the string by it's name. Therefore,

calling Trace(SHelloWorld); will print out "Hello world!".


The advantages of this follow:







  • Centralized - If someone needs to change a string

    that's displayed to the user, they can look for it in one (centralized)

    location, instead of scanning through all the code.





  • Multiple references - If a string is used over and over

    again, you only need to keep one copy of it in the string table.

    This not only saves memory, but it makes it easy to change

    all occurances of the string at once.







  • Faster code - A reference to a string table (SHelloWorld)

    is stored internally as a number, making it very fast to pass

    around. The string table reference is only converted to

    a string, "Hello world!", at the last moment.







  • Localization - This is the most important aspect of

    the string table. If a program only references SHelloWorld then

    translating the program to spanish only requires that

    the string table for SHelloWorld be changed from "Hello world!"

    to "Hola el mundo!".


  • Here's the really cool part... the string table can contain the

    string for both "Hello world!" and "Hola el mundo!". The version

    it uses depends upon the user's language. This allows your application

    to be written with English, Spanish, Japanese, etc. strings already

    translated. If someone in an English speaking country runs it,

    your program will use English. If they run it from Japan (or otherwise

    indicate they're Japanese), they get Japanese from the program.

    Furthermore, if MIFL is used for an online interactive fiction server, then

    users in the US will see English prompts, and users from

    Japan will see Japanese prompts, even though it's the same

    program running. (Their chat text won't be translated though.)


Before creating a string (in the string table), you should

first identify what languages your project will be

translated into:





  1. Select the "Project settings" menu item

    under the "Misc. menu.


  2. In the "Project settings" page that appears, you'll find

    a section for "Languages". This lists all the languages

    supported by your project.

  3. To add a new language, select the language

    underneath "Add language" and then

    press "Add language". You will see the

    language added in the list to the right.




To add a string to the string table, just:





  1. Select the "Add new string" menu item

    under the "Misc. menu.


  2. The "Modify string" page will appear.
  3. Type in the "Name", such as "SHelloWorld".







  4. Type in the string for each of the languages.

    You might end up leaving some languages blank because you

    don't know them and are relying on a translator.


  5. If a language's string is blank when the program is run,

    the first non-blank string will be used, even though it

    isn't for the right language.

  6. Revisit any code you wrote and replace all occurances

    of "Hello world!" with SHelloWorld.





  7. Compile your code and use the Test

    compiled code page to test that the strings still

    display.



Even if you added Japanese translation of "Hello world!", when

your HelloWorld() function was called, it probably printed

out "Hello world!". This is because it thinks you're in

an English speaking area. (Although theoretically if you're

in Japan it will automatically default to Japanese, and instead,

refuse to display the English version.)

To tell MIFL what language to use:





  1. In the "Test compiled code" page,

    type "LanguageSet(1041)" and

    press "Run this".




  2. The 1041 parameter is an ID for Japanese. English is 1033.

    The language IDs conform to the standard LANGID #defines in

    winnt.h. If you don't have this but wish to use the translations,

    please contact me.


  3. Calling HelloWorld() will now say "Hello world!" in

    Japanese, assuming that you had entered a Japanese translation

    for the string.





  4. To find out what language MIFL is using

    type "LanguageGet()" and

    press "Run this". The return value is the

    current language ID.






Warning: - Using a string table with multiple

languages can introduce some difficult-to-find bugs. For

example, look at this code:

MyGlobal = SHelloWorld;


Trace (MyGlobal);




This will write "Hello world!" to the debug trace if the

language is set to English, "Hola el mundo!" for Spanish, etc.

No problems here. Lets complicate matters:

MyGlobal = SHelloWorld + " " + SHelloWorld;


Trace (MyGlobal);




This will write "Hello world! Hello world!" to the debug trace if the

language is set to English, "Hola el mundo! Hola el mundo!" for Spanish, etc.

No problems here. Now for the problem:

LanguageSet (1033);


MyGlobal = SHelloWorld + " " + SHelloWorld;


LanguageSet (1034);


Compare = SHelloWorld + " " + SHelloWorld;


Trace (MyGlobal == Compare);




The trace output is false. One might expect

it to be true since the exact same sequence of operations

is used to produce MyGlobal as well as Compare. Why is this?

"MyGlobal = SHelloWorld + " " + SHelloWorld;" ends up converting

SHelloWorld into a string. Since the current language is English,

the English version of SHelloWorld will be used from the string table.

MyGlobal ends up with the string "Hello world! Hello world!".

However, "LanguageSet (1034);" changes the language to

Spanish. When "Compare = SHelloWorld + " " + SHelloWorld;" is called,

the Spanish versions of the string are used.

Compare ends up being "Hola el mundo! Hola el mundo!".

The two strings are compared (as strings). To the program

they're obviously not the same.

Resources
Resources are desgigned to hold data that isn't

a string and isn't code. This could include images,

sounds, etc.

Unforuntately, the type of resources a MIFL

project contains depends upon its context; different types

of resources will be available for interactive fiction than

for 3D modelling. Since this tutorial is written to be

context independent, it won't be very specific about the

types resources.

To create a resource:





  1. Select the "Add new resource" menu item

    under the "Misc." menu.



  2. A new page, "Add a resource", will appear. Click on one

    of the listed resource types. Unfortunately, I can't

    be detailed here.




  3. When you click on a resource, a new page will appear, "Modify

    resource". It lets you change the name of the resource as well

    adding resources for specific languages.

  4. Type in a "Name", such as "RMyResource". The

    "R" prefix indictes it's a resource.



  5. Type in a "Description" so that when you see

    the list of resources you'll know what it's for.



  6. Press "Add" for one of the languages. This

    will bring up a dialog that's specific to modifying the

    resource you have selected.




  7. If you have specified multiple languages for your project,

    you'll notice that the resource can contain slots for each

    of the languages. This lets the resource automatically

    adjust to the user's language, just as string tables did.

    If this were an audio resource with a recording of someone speaking,

    you could include several recordings, one for each

    supported language.

    And, just as with string tables, if you only fill in one

    resource entry, that one will be used, despite the user's

    choice of lanugages. You probably don't need localized versions

    of ever image, for example, although signs and other letters

    within the images might cause a problem.


Resource usage is context dependent, so I can't show that

here. As a general rule, the content (such as interactive fiction)

will provide a library specific to the context. It will

contain functions for using the resources the context supports.

Constructors and destructors


Describes how constructors and destructors work in MIFL.
MIFL supprots constructors and destructors for objects.

A constructor is called when an object is created, and

a destructor is called when an object is deleted (usually).

Classes and objects typically use constructors

to initialize variables and start timers. Destructors are usually

to unravel relationships the object has with other objects.


The use of a constructor and/or destructor for an object

is option. To use one, the object needs to support

the public method Constructor() and/or

the public method Destructor().

It may also want to support Constructor2().

Constructor() and Desctructor() are called under the following

circumstances:





  • When a program is first run, all of the automatically

    created objects are created. Then, Constructor() is

    called for all the objects.





  • If a running program has been saved to disk, and is reloaded,

    the Constructor2() method will be called for all

    the loaded objects instead of Constructor().





  • When an object is created using

    "new", Constructor() will be called for the object.



  • When an object is deleted using

    "delete", Destructor() will be called for the object.

    The destructor will be called before all the timers

    have been deleted.



  • If a program is shut down even though objects are still

    around, then Destructor() will not be called.




Datatype overview


Describes the different data types.
MIFL supports the following data types:
Boolean
A boolean can either be true or false.

Both true and false are keywords built into the language.
































































































































Convert data-type to boolean
Data-typeConversion
Boolean NA
Character if '\0' then false, else true
Function true
List true
List.Method true
Null false
Number if 0 then false, else true
Method true
Object true
Object.Method true
Resource true
String if "" then false, else true
String.Method true
String table if "" then false, else true
Undefined false


Character

A character is a unicode character, from 0 to 65535.

Characters are specific by using single-quotes around a single

character, such as 'x' or '?'.

The same escape sequences that strings use can appear in

a character, such as '\n'.
































































































































Convert data-type to character
Data-typeConversion
Boolean 't' if true, 'f' if false
Character NA
Function
List '\0'
List.Method '\0'
Null '\0'
Number convert to Unicode character
Method '\0'
Object '\0'
Object.Method '\0'
Resource '\0'
String first letter of the string
String.Method '\0'
String table first letter of the string
Undefined '\0'


Function
This is a reference to a function. Function names are

tokens generated at compile time.
































































































































Convert data-type to function
Data-typeConversion
Boolean undefined
Character undefined
Function NA
List undefined
List.Method undefined
Null undefined
Number undefined
Method undefined
Object undefined
Object.Method undefined
Resource undefined
String searches through the function list for the name
String.Method undefined
String table searches through the function list for the name
Undefined undefined


List

A list is an array of values (which can be any of the datatypes,

including other lists). For more information on lists, see

the tutorial about them.








Convert data-type to list
No conversions possible.


List.Method
A "List.Method" is a combination of a list and a method assocaited

with that list. Although this is a dataype, programmers won't

usually think of it as one since it's an intermediate step

towards accessing the list's methods, such as List.ListAdd();








Convert data-type to list
No conversions possible.

Null
The null type has no values.






Convert data-type to list
No conversions possible.



Number
A number is a floating point value (in C/C++ terms it's

an 8-byte double) that has 15 digits of precision and ranges

from (approx) -1e300 to 1e300.

A number can be written in the following forms:






  • Integer - A series of digits. A negative in front

    is optional.







  • Decimal - An optional negative, with (optional) digits followed

    by a '.' and more digits.







  • Exponential - A decimal number immediately followed by

    'e' and then an integer.







  • Hexadecimal - '0x' followed by any number of hexadecimal

    digits ('0'..'9', 'a'..'f').




































































































































Convert data-type to number
Data-typeConversion
Boolean 0 if false, 1 if true
Character Unciode value of the character
Function 0
List 0
List.Method 0
Null 0
Number NA
Method 0
Object 0
Object.Method 0
Resource 0
String convert the first characters of the string to a number
String.Method 0
String table convert the first characters of the string to a number
Undefined 0


Method

This is a reference to a method. Method names are

tokens generated at compile time.






























































































































Convert data-type to method
Data-typeConversion
Boolean undefined
Character undefined
Function undefined
List undefined
List.Method keep only the method
Null undefined
Number undefined
Method NA
Object undefined
Object.Method keep only the method
Resource undefined
String searches through the public method list for the name
String.Method keep only the method
String table searches through the public method list for the name
Undefined undefined

Object

This is a reference to an object.









Convert data-type to list
No conversions possible.



Object.Method

An "ObjectMethod" is a combination of an object and a method assocaited

with that list. Although this is a dataype, programmers won't

usually think of it as one since it's an intermediate step

towards accessing the object's methods, such as Object.MyMethod();

It is a useful construct for timers and other callbacks since

it remembers both the method to call and object to which it

belongs.







Convert data-type to object.method
No conversions possible.

Resource

A resource is a reference to a resource (in the project).

Resource names are generated at compile time.






Convert data-type to resource
No conversions possible.


String
A string is a reference to memory containing a Unicode string.

For more information on strings see the tutorial.

































































































































Convert data-type to string
Data-typeConversion
Boolean "false" if false, "true" if true
Character string with the one character
Function function name
List list elements, separated by commas
List.Method "list." with the method name appended
Null null
Number decimal form, or an exponential form if it's too large
Method method name
Object the return from Object.Name(), or the object's class
Object.Method the return from Object.Name(), or the object's class,

followed by "." and the method's name

Resource convert to string
String NA
String.Method "string." followed by the method's name
String table the string designated for the current language
Undefined ""

String.Method

A "String.Method" is a combination of a string and a method associated

with that string. Although this is a dataype, programmers won't

usually think of it as one since it's an intermediate step

towards accessing the string's methods, such as String.StringSlice();






Convert data-type to string.method
No conversions possible.

String table

A string table is a reference to a string table (in the project).

String table names are generated at compile time.






Convert data-type to string table
No conversions possible.


Undefined

The undefined type has no values.








Convert data-type to undefined
No conversions possible.


Get() and Set() code


Describes how to intercept get() and set() calls to global variables and properties.
Typically, when a global variable or an object's property is

created, the variable's contents can be accessed directly.

Any function or method can change the variable (or method)

to any value.


You may wish to protect variables (or methods) so that only

valid values can be written. Alternatively, you might

need the accessing of a variable to run some code. (For example:

The "weight" property of a backback might sum of the weight

of its contents and its own weight.)


You can do such interceptions by providing "get and set" code

for the variable or property. There are two ways to do this:



  • Check the "Use get/set code" in the variable's or property's definition


  • Use some run-time calls to change the get/set code for variables or properties




"Use get/set code" checkbox
Providing your own get/set code for a global variable or

an object's property is easy. If you are modifying a global

variable you will find a checkbox for "Use get/set code" and

one button each for "Get" and "Set. If you are modifying an

object's properties, you'll find a similar checkbox and buttons.

To provide your own get/set code:





  1. Check the "Use get/set code" checkbox.





  2. Press the "Get" button to modify the code to

    get the variable.



  3. Press the "Set" button to modify the code to

    set the variable.



Note: When you attach get/set code to a variable or method,

any reads from or writes to the variable will access the

get/set code. Except, from within the code for handling get

and set itself; this will be able to access the variable directly.

Run-time calls
Modifying the get/set code for a global variable or

object's property at run-time is slight more difficult.


To modify a global variable's get/set code you need

to call GlobalGetSet(). See the function's definition

for more information.


To modify an object's property's get/set code you need to

call LayerPropGetSetAdd(). See the

method's definition for more information.




Load/save objects


Describes how to loading/saving objects from/to disk works.
MIFL lets you save one or more (or all) objects to disk, and

then reload them at a later point. The exact function needed

to do this depends upon the context, so I can't go into

details here.


However, I can explain the basic process:




Saving
When one or more objects are saved, the following steps are taken

for each object:







  1. Identify the initial properties in a virgin object - A "virgin"

    form of the object is created using the object's class (from the

    lowest-priority layer). The virgin object has its properties

    initialzed according to the "initialized to" values for

    the object's methods.





  2. Save only property changes - The to-be-saved

    object is compared to its "virgin" version. Only those properties

    that have been changed (or deleted) will be saved.


  3. This is a very useful feature since it not only reduces the

    space necessary to save objects, it makes it safer for authors

    to save all the objects, change some properties, and then

    reload the previous objects (which were created with old code).

    Since only changed (or deleted) properties were saved, the

    realoded objects will contain all the properties as prescribed

    by the new code, except for the changes.

    Changing the code between a save and load isn't 100% safe

    though, and problems can arise. For example: If a saved

    object relied on the class "Unhealthy", but when it reloaded

    discovered that no "Unhealthy" class exists, it will ignore

    the "Unhealthy" class.




  4. Save all layers - All the layers of the object

    are saved, including any single-instance methods and get/set

    code.





  5. Save contained in - The object reference of the

    object's container will be saved. The contained objects'

    references are not, however, because this information

    can be reconstructed from the containers.







  6. Save all timers - All the object's timers are

    saved. The timer's suspended state is also saved.





In addition, if all the objects are saved, the following

information is also saved:





  1. Global variable changes - Just as only changed

    (and deleted) properties are saved, so too with global variables.

    Any unchanged global variable is ignored.





A note about numerical accuracy: - Because of the

wave data save is currently implimented, numbers may be rounded

off to the nearest 0.00001. This is not a problem for most

numbers, but might cause problems under some circumstances.


Loading

If all the objects were saved, the following

information is loaded:





  1. Global variable changes - All the global variables

    are created and set the the "initialized to" value from compile.

    Then, any changes are loaded from the saved version.

    If any globals were deleted prior to saving then they're

    deleted.



When the saved objects are reloaded, the following steps happen

for every object:





  1. Check for object ID conflict - If there is a conflict

    in the object's ID with an object that already exists, then

    a new ID is created. The object's ID is changed to the new ID,

    and any saved object (or data) that references the old ID gets

    updated.





  2. Create layers - The loaded object has all the

    proper layers setup.







  3. Create properties - All the properties (from the

    lowest priority layer) are added using the compiled

    "initialized to" values.





  4. Load property changes - Any changes to the

    properties are loaded. This also includes property deletions.







  5. Load contained in - The contained-in reference

    is loaded and the loaded object is placed in the container.

    If the container no longer exists then the loaded object

    is not placed in any container.







  6. Load all timers - All the object's timers are

    load. The timer's suspended state is also loaded.







  7. Call Constructor2 - If the object supports

    a Constructor2() method, that is called so the object knows

    it has been loaded from a saved state.




Loop and stack limits


Talks about limits to loops and stack depth.
MIFL performs the following checks to prevent infinite loops:





  • Loop counts - For most contexts, MIFL will

    abort a loop if it is more than a million iterations.

    This protects against infinite loops.





  • Call-stack depth - For most contexts, MIFL will

    abort a function or method call if it is more than 1000

    function/method calls deep. This protects against infinite

    recursion.








Download 8.87 Mb.

Share with your friends:
1   ...   100   101   102   103   104   105   106   107   ...   151




The database is protected by copyright ©ininet.org 2024
send message

    Main page