Two ways come to mind:
I simplified the Tavern example, for simplicity's sake. The Look external script (again, script 339) is actually:
(public
SaidLookTavern 0
TavernLookAround 1
TavernLookAtStool 2
)
(procedure (SaidLookTavern)
(cond
((Said '[<at,around][/!*,room,building,tavern,establishment]')
(TavernLookAround FALSE FALSE)
)
;snip
)
(DisposeScript TAVERN_LOOK)
)
(procedure (TavernLookAround firstTime doDispose)
(HighPrint 339 22)
;The grimy window lets little light into this tavern. It smells like stale ale and other more unpleasant things.
;The floor is covered with dirt, and the bar with sticky beer.
(HighPrint 339 23)
;Smoke appears to be rising from the center cask behind the bar. To your right, two gamblers are playing cards.
(if firstTime
(HighPrint 339 24)
;The bartender glares at you as you enter, and so does the ugly goon on the left. You get the impression that you are not welcome.
)
(if doDispose
(DisposeScript TAVERN_LOOK)
)
)
Then in the main Tavern.sc (script 331), when you enter the tavern for the first time, it will call (TavernLookAround TRUE TRUE), which will add the extra bit about the bartender glaring at you, then will unload itself from memory. So you might be able to rework your local variables enough that you can call global procedures passing those local variables as references. I don't honestly know the memory impact global procedures have vs global variables, but I would assume procedures is less.
The other thing that comes to mind is using Btst, Bset, Bclr global flags, instead of global variables. You may have some global variables that are only used for TRUE/FALSE values, which could be condensed into flags instead. A flag only uses 1 bit of memory, whereas a global variables uses 16 bits.