How does King’s Quest VI do that thing where if the Narrator says something, it includes a fancy drop cap? Well, it’s sorta similar to how KQ5 did, but implemented better. Mostly because it’s a newer game and not basically the first on a new engine.
Requirements: a view full of letters, all the same size, and a font where the tab character is as wide as one of these letters.
First, some technical backstory. Narrator
is a special class with methods to speak a specific message. It will decide on its own if it should display the passed message in a window of some kind, or play it back as spoken audio. It will also handle timing, disposing of the window and everything at the end. The messages themselves are provided by the Messager
class.
A Talker
is a kind of Narrator
that builds on the above to add talking heads. Fortunately we won’t have to go any deeper into those because this post is all about KQ6’s drop caps, and it’s only Narrator
who uses those.
Specifically, KQ6 uses a special DropCapNarrator
, in fact. It’s just like a regular Narrator
, but overrides the display
method to add some extra bits:
(class DropCapNarrator of Narrator (properties ; Narrator properties elided. strView 945 ; the view with the letters in ) (method (display theText &tmp theTalkWidth newWindow theFirst theDropCap) ; Ensure we don't clip the screen edge. (if (> (+ x talkWidth) 318) (= theTalkWidth (- 318 x)) else (= theTalkWidth talkWidth) ) ; Clone up a new copy of one of those fancy woody windows. ((= newWindow (gWindow new:)) color: color back: back ) ; That is, set newWindow's colors to our own. ; If we have mouse support and we're not using the invisible ; cursor, remember what we -do- use and go invisible. (if (and (not (HaveMouse)) (!= gCursorNumber 996)) (= saveCursor gCursorNumber) (gGame setCursor: 996) else (= saveCursor null) ) ; Now we're ready for the magic. ; Grab the first letter of the message text we're to show. (= theFirst (StrAt theText 0)) (if (and (>= 90 theFirst) (>= theFirst 65)) ; The first character is between 'A' and 'Z' inclusive. ; Replace that first character with a tab. (StrAt theText 0 9) ; Remember, the tab is as wide as a drop cap! ; Create a new DIcon for our cap and set it up. ((= theDropCap (DIcon new:)) view: strView ; Loop 0 is A to M, loop 1 is N to Z. loop: (+ 0 (/ (- theFirst 65) 13)) cel: (mod (- theFirst 65) 13) ) ; Now display our message on screen... (Print window: newWindow posn: x y font: font width: theTalkWidth title: (if showTitle name else null) ; ...but with the text shifted down a little... addText: theText 0 7 ; ...and that DIcon on top of it. addIcon: newDIcon 0 0 0 0 modeless: true init: ) else ; Not a valid drop cap so we can skip all that. (Print window: newWindow posn: x y font: font width: theTalkWidth title: (if showTitle name else null) ; See? No positioning or DIcon here. addText: theText modeless: true init: ) ) ; The rest is up to the regular Narrator. ) )
Personally, I’d set up most of those properties, then stop and consider if we have a drop cap. Duplicating all those sends… it’s still better than what KQ5 did.