Articles Archive
Articles Search
Director Wiki
 

Automating Your Authoring, Part 1

July 11, 2001
by Danny Kodicek

Many people are unaware of the amount of time they could save in their work by using Lingo to automate some of the more tedious tasks. Renaming cast members, resizing objects, changing text and cutting up bitmaps are some of the tasks which we have to do every day that take up a lot of time without requiring any of our intelligence. Writing quick author-level scripts can save a lot of work. I'm going to be looking at how to write these simple scripts and some of the Lingo commands that are useful. I'm also going to look briefly at Score Recording, another very powerful feature that enables you to make authoring changes to the Score via Lingo.

The Basics

Let's start with something simple. Let's suppose your basic button behaviour assumes that the rollover state of the button called "play" will be called "play roll" and the down state will be "play down", but your designer has exported 300 new button files named on the model "play", "play_roll" and "play_down". You could change the behaviour, but there are already a lot of other buttons in the movie and you don't want to risk breaking existing code, so instead you decide to rename the new buttons to fit your scheme. Doing it by hand would be a nightmare, so this seems like the ideal moment to do it via Lingo.

Let's make a first try at a script that will do it.

on renamebuttons

  repeat with j = 1 to the number of castlibs
    repeat with i = 1 to the number of members of castlib j
      mem=member (i, j)
      nm=mem.name
      repeat with k = 1 to nm.length
        if nm.char[k] = "_" then put " " into nm.char[k]
      end repeat
      mem.name=nm
    end repeat
  end repeat

end

Once you have entered this in a movie script, all you need to do is open the message window and type:

renamebuttons

It should be fairly clear what this does. It loops through each cast member of each castlib, checks if the name of that cast member contains an underscore, and if so, replaces it with a space.

Now, this works fine, but there are some dangers. The most important of these is that if any of your other cast members happen to have underscores in them then they will also be renamed. That might be a big problem. So instead, let's restrict ourselves to just the cast members that we have selected in the cast. We'll also make the renaming a little more efficient by using the offset function (not that it would make much of a difference in this case)

To operate only on selected cast members, we can use the selection of castlib property. This returns a list of lists: [[1, 4], [6, 8]] would mean that currently cast members 1, 2, 3 and 4, and cast members 6, 7, and 8 are selected. Just to be on the safe side, I'm also going to make the function only work on one castlib. This is because there may be other cast members selected in other castlibs which you're not interested in. So here's the new handler:

on renamebuttonsbylib lib

  if voidp (lib) then lib= the activecastlib
  s=castlib (lib).selection
  repeat with lyst in s
    repeat with i = lyst[1] to lyst[2]
      mem = member (i, lib)
      nm = mem.name
      off = offset ("_", nm)
      if off <> 0 then put " " into nm.char[off]
      mem.name=nm
    end repeat
  end repeat

end

To run this one, you need to pass it a castlib name or number, for example

renamebuttonsbylib "buttons"

The script runs through all selected cast members in that library and renames them if necessary. You'll also notice that I included a line that used the default value of the activecastlib for the lib variable. This means that if you don't pass a castlib reference, it will look through the most recently selected cast by default.

This basic technique is one you can use to automate any number of different tasks. Here's one that will use imaging Lingo to transform all 32-bit bitmaps to 16-bit, retaining their registration points:

on transformbitmaps lib

  if voidp (lib) then lib= the activecastlib
  s=castlib (lib).selection
  repeat with lyst in s
    repeat with i = lyst[1] to lyst[2]
      mem = member (i, lib)
      if mem.type <> #bitmap then next repeat
      im = mem.image
      if im.depth = 32 then
        im2=image (im.width, im.height, 16)
        im2.copypixels (im, im.rect, im.rect)
        reg = mem.regpoint
        mem.image = im2
        mem.regpoint = reg
      end if
    end repeat
  end repeat

end

Incidentally, this script uses the member.type property to check whether the member being looked at is a bitmap. This kind of check is often useful. So here's one final example in this mould: let's make a routine that will convert all your text members to field, retaining basic formatting. This uses the new command to create a completely new cast member, which is a very powerful technique. The script also includes two additional options: whether to retain a copy of the original member, and whether the new fields should be in the same cast member positions as the originals. Both of these default to true.

on converttofield lib, retain, sameplace

  if voidp (lib) then lib = the activecastlib
  if voidp (retain) then retain = 1
  if voidp (sameplace) then sameplace = 1
  s=castlib (lib).selection
  repeat with lyst in s
    repeat with i = lyst[1] to lyst[2]
      mem=member (i, lib)
      if mem.type <> #text then next repeat
      if sameplace then
        newmem = new (#text)
        newmem.media = mem.media
        newmem.name = mem.name
        erase mem
        mem=newmem
        f=new (#field, member (i, lib))
      else
        f = new (#field)
      end if
      f.text = mem.text
      f.rect=mem.rect
      f.alignment = string (mem.alignment)
      f.name = mem.name
      repeat with k = 1 to mem.text.length
        the font of char k of field f = mem.char[k].font
        the fontsize of char k of field f = mem.char[k].fontsize
        s = mem.char[k].fontstyle
        tx = ""
        repeat with sym in s
          tx = tx & sym & ","
        end repeat
        if tx.length > 0 then
          delete tx.char[tx.length]
          the fontstyle of char k of field f = tx
        end if
        
      end repeat
      if not retain then erase mem
    end repeat
  end repeat

end

Selecting the cast members you are interested in is not the only way to narrow down the search, by the way - Lingo has access to all the properties of the cast member. You could use the creationDate property to change only cast members that were imported in the last week, or the modifiedBy property to change only members which have not already been modified, for example.

Quick on the Draw

Authoring scripts are not just useful for adapting or altering existing assets. You can also use them to create graphics from scratch. Recently, I worked on an educational website about Maths which included a large number of geometrical figures, and quickly grew tired of going into Photoshop to create the graphics. So I created some routines that allowed me to draw geometrical figures quickly from within Director. Here's an example:

on drawsquare w, nm, col, bg, s

  v=new (#vectorshape)
  v.vertexlist = [[#vertex: point (0, 0)], [#vertex: point (0, w)], [#vertex: point (w, w)], [#vertex: point (w, 0)]]
  if voidp (bg) then bg = rgb (255, 255, 255)
  if voidp (col) then col = rgb (0, 0, 0)
  if voidp (s) then s = 1
  v.backgroundcolor = bg
  v.strokecolor = col
  v.strokewidth = s
  v.closed = 1
  v.fillmode = #none
  mem = new (#bitmap)
  mem.image = v.image
  mem.name = string (nm)
  erase v

end

This example is pretty simple, but you can take this kind of process a long way. How about this: a method to draw a maze. We feed it with a list of lists in this format: [[1, 2], [0, 1]]. Each number represents a single square of the maze, and has a value representing whether there is a wall to the right or to the bottom of that square. So:

0 = exit at bottom and right

1 = exit at bottom

2 = exit at right

3 = no exit to bottom or right

We don't need to worry about exits to top and left because these are redundant, and we assume the maze is closed on all sides. So let's draw it.

on drawmaze m, w

  if voidp (w) then w = 20
  vert = m.count
  hor = m[1].count
  maze = image (w * hor + 1, w * vert + 1, 16)
  repeat with i = 1 to vert
    repeat with j = 1 to hor
      sq = m[i][j]
      if sq mod 2 or i = vert then maze.draw ((j - 1) * w, i * w, j * w, i * w + 1, rgb (0, 0, 0))
      if sq > 1 or j = hor then maze.draw (j * w, (i - 1) * w, j * w + 1, i * w, rgb (0, 0, 0))
    end repeat
  end repeat
 
maze.draw (0, 0, 1, w * vert,
rgb (0, 0, 0))
  maze.draw (0, 0, w * hor, 1,
rgb (0, 0, 0))

  mem = new (#bitmap)
  mem.image = maze

end

Try this out - type in the Message Window:

drawmaze ([[1, 0, 3, 0], [0, 3, 0, 0], [0, 0, 2, 0]])

Check in your Internal cast and a new maze will have been added. Incidentally, this technique isn't just useful in Authoring - it can be used at runtime too, to save filesize. But don't forget that every new cast member you add at runtime has to stay in memory until erased, so it can impact on performance. Make sure you erase your cast members once you are done with them.

Next week, we'll get to that score recording.

A text file containing the scripts for this article is available. Just copy and paste into your Script window.

Danny is a mathematician by training, an occasional writer, actor and, for the last four years, programmer. He is self-taught in Director and it occasionally shows. He is part of the company Wellspring Interactive Ltd and has just finished working as Head of Clues on their first major project, TimeHunt.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.