$LastChangedDate: 2010-04-23 17:34:40 -0400 (Fri, 23 Apr 2010) $

Generic guide for Lua coding.

Lua coding rules:
-----------------

- Write "self.member" for data members.

- Write "self:Method()" to call a member method.

- Write ":" when defining and calling class methods

  This is a good habit for coding consistency and maintainability.
  "self" will be automatically defined in a method's body.
  Colon can be written whether or not a class can have instances.
  What seemed like a good style was to write "." with singletons.
  But that is inconsistent and would break if singleton is rewritten for instances.
  Importantly, writing "object.Method()" (dot) will fail if the method uses "self".

- Instantiate a Lua class after all of its methods are defined.

- To derive a class, use lib.DerivedClass().

Lua coding style:
-----------------

- Camel-back names.

- Name ctors,dtors as New(),Destroy().

- "local" can be used in a chunk, outside a function, similar to C/C++ "static",
  to avoid polluting global table.

PITFALLS:
---------

Remember that Lua delays garbage-collection.
An example of this pitfall is:

function Game:Reset()
    ...
    self.playerReplay = Replay:New( PlayerActor.aircraft )
end

The intent is to re-create a Replay object at reset.
Although reassigning self.replayReplay unreferences the first Replay,
it will persist in C++ -- Lua has only scheduled it for garbage-collection.
The C++ Replay class has registered timer-tick functors which continue
to occur in C++ while it seems dead in Lua.  The problem is that
the old Replay contends with the new one for control of the player's Craft!

Calling garbagecollection() isn't the total solution,
because it doesn't necessarily destroy all garbage.

An adequate-but-not-ideal solution in the example was to add
the C++ method Stop() which disables its timer-ticks.

function Game:Reset()
    ...
    self.playerReplay:Stop()
    self.playerReplay = Replay:New( PlayerActor.aircraft )
end

Examples:
---------

-- The "." indicates that Keyboard has no instances (Singleton):
Keyboard:Handler()

-- The ":" indicates this an instance:
aircraft:GetPosition()

-- Wrong style:
Hud:Init()  -- Hud is a singleton but ":" implies an instance/self

Lua interpreter was altered:
----------------------------
Since writing "!=" instead of "~=" is such a common mistake,
the parser of Lua 5.1.3 was hacked (llex.c) so that it recognizes "!=".

Example of lib.DerivedClass() and lib.NewInstance():
----------------------------------------------------
lib.DerivedClass() produces a class table.
lib.NewInstance() produces an instance table.

-- The absolute base class begins with an empty table.
Actor = { }

-- ctor
function Actor:New( name, teamNum, object )
    -- If base class is being instantiated, the base class will be passed as self.
    -- Or, the derivative is being instantiated, which chains ctors by passing its instance as self.
    if ( self == Actor ) then
        self = lib.NewInstance( Actor )  -- not chained, need new instance
    end

    self.name    = name
    [...]

    return self
end

-- lib.DerivedClass() produces a "class table" that inherits from another "class table".
-- Derived becomes a metatable, inheriting from Base, from which instance tables can be produced.

AircraftActor = lib.DerivedClass( Actor )

-- ctor.
function AircraftActor:New( name, teamNum, aircraftType, RadarClass )
    -- Make "instance table" from derived metatable.
    if ( self == AircraftActor ) then  -- either instantiate or use derivative's instance
        self = lib.NewInstance( AircraftActor )
    end

    -- The instance table is empty.
    -- Chain to the base ctor to create and initialize its members.
    Actor.New( self, name, teamNum, nil )

    [...]

    return self
end

lib.NewInstanceChain():
-----------------------
Creates an "instance table" from a "class table" unless
self is an instance (of a derived class) rather than class table.
It solves the problem: is an instance of the base being constructed,
or is a base constructor being chained by derivative constructor?

The reason for NewInstanceChain() is best explained by an example of code:

function lib.NewInstanceChain( class, self_ )
    assert( self_ )
    if ( self_ == class ) then
        return lib.NewInstance( class )  -- class was passed implicitly by "Class:New()"
    else
        return self_  -- self was passed explicitly by "Base.New( self )" by derived constructor
    end
end
function Base:New()
    self = lib.NewInstanceChain( self, Base )
end
function Derived:New()
    self = lib.NewInstance( Derived )
    Base:New( self )   -- pass derived instance to base constructor (chaining)
end
obj0 = Base:New()      -- NewInstanceChain() makes a new instance
obj1 = Derived:New()   -- NewInstanceChain() is a NOP (passes back instance of derived class)
