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.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.