The Dink Network

Reply to Re: magicman's rant about scripting techniques

If you don't have an account, just leave the password field blank.
Username:
Password:
Subject:
Antispam: Enter Dink Smallwood's last name (surname) below.
Formatting: :) :( ;( :P ;) :D >( : :s :O evil cat blood
Bold font Italic font hyperlink Code tags
Message:
 
 
May 5th 2014, 12:26 PM
custom_magicman.gif
magicman
Peasant They/Them Netherlands duck
Mmmm, pizza. 
There seems to be some interest in more of these things, so I've started writing a rant about how and why I wrote Persistent Loot the way it is. I'll probably finish it later this week. Until then, have a mini-rant about a cool technique that you can use with "Fancy functions":

Introducing: Optional parameters

Let's start with a bit of code:

// stuff.c
void remove( void )
{
  int &ednum = sp_editor_num(&current_sprite);
  if (&ednum > 0)
  {
    editor_type(&ednum,1);
  }
  sp_nodraw(&current_sprite,1);
  sp_nohit(&current_sprite,1);
  sp_active(&current_sprite,0);
}


Now, a call to external("stuff","remove"); will remove &current_sprite from the game. Now, in a lot of cases, this is what you want. In some cases, I'd love to remove a different sprite from the game. So. What are my options?

Change &current_sprite to &arg1 in void remove( void ), then add &current_sprite as an argument everywhere I call it using external()? That seems like a good option, though I don't look forward to doing a big search-and-replace.

Not use external() at all for those few cases? It would work, but that kind of beats the purpose of separating out code.

In my opinion, the best solution to this problem is to consider what happens when you don't pass a parameter, but use &arg1 anyway: its value will be 0. Since 0 is never a valid sprite number, we can check for that, and deal with it accordingly. The code is now:

// stuff.c
void remove( void )
{
  // Set new variable (with descriptive name!) to default value.
  int &sprite = &current_sprite;
  if (&arg1 != 0)
  {
    // Oy, we actually have a value. Better set the variable to that.
    &sprite = &arg1;
  }

  int &ednum = sp_editor_num(&sprite);
  if (&ednum > 0)
  {
    editor_type(&ednum,1);
  }
  sp_nodraw(&sprite,1);
  sp_nohit(&sprite,1);
  sp_active(&sprite,0);
}


Alternatively, the first bit can set &sprite to &arg1, and only set it to &current_sprite if &arg1 turns out to be 0.

When designing procedures with default arguments, I recommend that you order the arguments by how often you expect to use a non-default value. For example, if you have a procedure with 3 arguments, of which the first is required, and the second and third have a default, you can pass in the first two, and the third will use the default value.

The main caveat: Occasionally, you may want to pass in 0! Sadly, external("stuff","remove"); and external("stuff","remove",0) do exactly the same thing, and are indistinguishable.

Also beware when entering variables, the following code may have dire consequences:

void talk( void )
{
  int &sprite = get_sprite_with_this_brain(9, &current_sprite);
  external("stuff","remove",&sprite);
}


If there are no brain 9 sprites on the screen, get_sprite_with_this_brain will return 0. This means that &current_sprite will be removed from the game!

Summary

If you don't pass an argument to a call to external(), its corresponding &arg1 to &arg9 variable will be 0. You can check for this, and do something sensible as default.

Beware when you want to pass 0, and for cases where you can accidentally pass 0.