Namespaces - KidneyThief/TinScript1.0 GitHub Wiki

Table of Contents

Description

  • All objects are instances of a C++ class. We use namespaces to add new methods and members that were not part of the original code class definition.
  • Being able to add new methods and members, extending the functionality of an object, without shutting down and recompiling / restarting, is at the heart of runtime development, as iteration and debugging can be so much faster.
  • A namespace in TinScript is a collection of members and methods, connected hierarchically to a parent namespace. This could almost be how we define a C++ class, and in fact, it parallels the hierarchy of the virtual tables in single inheritance.
  • We don't use the word 'class', because here, we don't define a class as in a C++ header - but rather, simply begin implementing function definitions - and they are automatically added to the collection of functions that share the same namespace.

Methods

  • The '::' separating two identifiers in a function definition denotes a namespace method, so a complete definition looks like:
    • <return type> <namespace>::<method name>(<arg1>, <arg2>, ...) { <function body> }
    • void MyNamespace::MyMethod() { Print("hello ", self.GetObjectName(); }
  • Calling a method uses the '.' operator, just like c++. Given an object test_obj, and a method (such as the example ::MyMethod() above), you call call the method using the syntax: test_obj.MyMethod();
  • Examples:
    • // OnCreate() and OnDestroy() are the only two methods automatically called
      void Foobar::OnCreate()
      {
          Print("Any object named 'Foobar' will automatically call me when created.);
      }
      object test_obj = create CScriptObject("Foobar");
    • // Any other method can be defined in a namespace, and explicitly called
      void Foobar::TestMethod()
      {
          Print("Executing 'obj.TestMethod()' will call me, if the object's name is 'Foobar');
      }
      test_obj.TestMethod();

Members

  • Just like methods, we can add members to an object that were not part of the coded class definition. Members are declared, just like any other TinScript variable, except we use the 'self' keyword create the new variable as a member of a specific object, and only from within a method.
  • It is strongly recommended that you only declare new members within the ::OnCreate() method, as this is guaranteed to be called, and all objects sharing the same namespace will have set of dynamic members.
  • Lets create an example using a character that has health:
    • // define a new member 'health'
      void Character::OnCreate()
      {
          int self.health = 100;
          Print("I am full of life!");
      }
  • Add a damage method
    • void Character::OnDamage(int damage_amount)
      {
          self.health -= damage_amount;
          Print("My health is now ", self.health);
          if (self.health <= 0)
              self.OnDeath();
      }
    • void Character::OnDeath()
      {
          Print("I've had better days...");
      }
  • Execute:
    • object test_player = create CScriptObject("Character");
      • output: I am full of life!
    • test_player.OnDamage(25);
      • output: My health is now 75
    • test_player.OnDamage(25);
      • output: My health is now 50
    • test_player.OnDamage(25);
      • output: My health is now 25
    • test_player.OnDamage(25);
      • output: My health is now 0
      • output: I've had better days...

LinkNamespaces

  • The hierarchy of methods and members for an object isn't limited to just one extension above code. Just like a hierarchy of class definitions in C++. You can link a child namespace to a parent, allowing an object access to all the parent methods, and overriding what is needed for the child.
  • In addition, you can explicitly call a method in any namespace by explicitly specifying the namespace as part of the call, instead of the self.
  • As in declaring new members for an object, it is highly recommended that you only LinkNamespaces from within the ::OnCreate() method, and that you immediately call the parent ::OnCreate().
  • Extending on the above example, supposed we want to create a shielded character that only takes a percentage of the damage. First we define an OnCreate() ShieldedCharacter, and link it to the parent.
    • ShieldedCharacter::OnCreate()
      {
          // link our namespace to the parent
          LinkNamespaces("ShieldedCharacter", "Character");

          // call the parent OnCreate() to maintain the hierarchy
          Character::OnCreate();

          // add a mitigation percent member for our shield
          float self.shield_strength = 30.0f;
      }
  • Now we need to apply the shield, which would happen during OnDamage
    • ShieldedCharacter::OnDamage(int damage_amount)
      {
          // calculate the amount of damage mitigated
          int shielded_damage = damage_amount * self.shield_strength;
          int actual_damage = damage_amount - shielded_damage;
          Print("My shield absorbed ", shielded_damage);

          // now allow the parent class to apply the actual damage
          Character::OnDamage(actual_damage);
      }
  • Execute:
    • object shield_character = create CScriptObject("ShieldedCharacter");
      • output: I am full of life!
    • shield_character.OnDamage(50);
      • output: My shield absorbed 15
      • output: My health is now 65
  • While you can link namespaces indefinitely, you cannot create circular links (A is linked as a child of B, B is a child of C, C is child of A...) or you'll assert.
  • Neither can you change the change what a scripted namespace is linked to, once it has been set, or any object created using the original linked hierarchy, would have it's inheritance changed, which would be extremely error prone and unpredictable.
⚠️ **GitHub.com Fallback** ⚠️