User-defined functions

Megalo's bytecode supports user-defined functions: reusable pieces of script code that can be "called" from multiple different locations. User-defined functions are useful for avoiding code duplication, if you have tasks that need to be performed consistently at different points in your script; they are also a good way to repeat tasks.

User-defined functions run with the "environment" of their caller. For example, a user-defined function called from inside of a for each object loop can access the current_object value. User-defined functions can't take arguments or return values.

This code sample creates five skulls and pushes them upward semi-randomly:

--
-- Define the function:
--
function my_cool_code()
   global.object[1] = global.object[0].place_at_me(skull, none, none, 0, 0, 0, none)
   global.object[1].push_upward()
   global.object[1].push_upward()
end

--
-- Call the function five times:
--
my_cool_code()
my_cool_code()
my_cool_code()
my_cool_code()
my_cool_code()

Scoping

Like aliases, user-defined functions are block-scoped: they are only available from inside of the block in which they were defined.

--
-- Let's loop over every player. If a player is holding an Assault Rifle, replace it 
-- with a DMR; but if they're already holding a DMR, replace it with an Assault Rifle.
--

for each player do
   alias current_weapon = allocate temporary object
   
   --
   -- We need to run the same code for both of the player's weapons, so let's define 
   -- a helper function so that we can avoid copying and pasting.
   --
   
   function swap_weapon()
      alias swapped_weapon = allocate temporary object
      
      swapped_weapon = no_object
      if current_weapon.is_of_type(assault_rifle) then
         swapped_weapon = current_player.place_at_me(dmr)
      end
      if current_weapon.is_of_type(dmr) then
         swapped_weapon = current_player.place_at_me(assault_rifle)
      end
      current_weapon.delete()
      current_player.add_weapon(swapped_weapon)
   end
   
   current_weapon = current_player.get_weapon(secondary)
   swap_weapon()
   
   current_weapon = current_player.get_weapon(primary)
   swap_weapon()

end

--
-- The `swap_weapon()` function was defined inside of the loop. Now that we're outside 
-- of the loop, the function has gone "out of scope;" we can't call it anymore. This is 
-- useful for organizing code: if a function is only used in a small part of your script, 
-- then it's best to keep it near that part of your script.
--

Recursion

User-defined functions can be recursive: that is, they can call themselves in order to run under specific conditions:

--
-- This function will perform a task "global.number[0]" many times:
--
function spawn_skulls()
   global.object[1] = global.object[0].place_at_me(skull, none, none, 0, 0, 0, none)
   global.object[1].push_upward()
   global.object[1].push_upward()
   --
   global.number[0] -= 1
   if global.number[0] > 0 then
      spawn_skulls()
   end
end

--
-- Let's spawn ten skulls:
--
global.number[0] = 10
spawn_skulls()

--
-- Let's spawn a random number of skulls:
--
global.number[0]  = rand(19)
global.number[0] += 1 -- ensure it's at least one
spawn_skulls()

When writing recursive functions, you need to be careful to ensure that the function actually ends: there must be some circumstance under which the function does not call itself anymore.

Megalo doesn't have the ability to loop over a numeric range; however, as shown above, recursive functions can be used as a substitute.

Availability

User-defined functions are supported by Megalo's bytecode — that is, the format that Megalo is compiled into. However, they did not exist in Bungie's actual design for the Megalo language. Bungie originally described Megalo as being similar to BASIC, with no loops, functions, or other jumps; the goal was to avoid constructs that could lead to infinite loops or similar softlocks. 343 Industries released an official editor, MegaloEdit, and this description is certainly accurate: the language's only real form of control flow is conditions and nested blocks. Nested blocks are compiled as disconnected units, with the outer block "calling" the inner block; user-defined functions are just blocks that are called from (or "nested under") multiple places.

As such, you won't see user-defined functions in official content, and other community-made tools made for Halo: Reach may fail to handle them properly. As far as I am aware, user-defined functions are entirely new to ReachVariantTool.