Logo Pending


Skip a bit, brother

Ah yes. The skip button. You don’t see those often in most of the old Sierra adventure games, and to be honest I’m not interested enough in the later ones to check. Sue me. But how do they work?
As usual, let’s look at the scripts.

First, we have Leisure Suit Larry 5 – Passionate Patti does Pittsburgh, which shares its skip system with Freddy Pharkas Frontier Pharmacist. The only notable difference between the two is that, being an SCI11 game, the latter uses Messages instead of Text resources. This system has two dedicated parts to it, plus how the current scene reacts:

(instance icon5 of IconI
  ; ...
  (method (select)
    (return
      (if (and gFFRoom (super select: &rest))
        ; That is, if we had a gFFRoom set and the usual response to a button click was true.
        (gIconBar hide:)
        (if (Print "Do you really want to skip ahead?" #title "Fast Forward" #button "Yes" 1 #button "Oops" 0)
          (if (== gFFRoom 1000)
            ; In this case, we want to cue something.
            (if (IsObject gFFScript)
              (gFFScript cue:)
              (SetFFRoom 0)
            else
              (Print "ERROR: Object passed to SetFFRoom isn't an object you silly person!")
            )
          else
            ; In the *other* case, we just want to go somewhere.
            ; This option is good for larger cutscenes.
            (gRoom newRoom: gFFRoom)
            (= gFFRoom (+ gFFRoom 1000))
            ; ... I'm... not entirely sure what that was for.
          )
        )
      else
        (return 0)
      )
    )
  )
)
 
(procedure (SetFFRoom room script)
  (if (not room)
    (= gFFRoom 0)
    (= gFFScript null)
    (gIconBar disable: 5)
  else
    (= gFFRoom room)
    (if (and (> argc 1) (== room 1000))
      (= gFFScript script)
    )
    (gIconBar enable: 5)
  )
)

Call SetFFRoom with anything but 1000, and you set up a skip to another room. Call it with “room” 1000 and a cue-able object otherwise. Pretty simple, I don’t think I need to bother with a practical example.

Incidentally, this makes one of the examples of a non-standard procedure whose name is absolutely certain.

On to Leisure Suit Larry 6 – Shape Up or Slip Out. This is the low-res SCI11 version, but I sincerely doubt the SCI2 version is much different. It has only one shared part, the icon, without a setup procedure. Note that icon5 is exported as ScriptID 0 8, hence the references throughout.

(instance icon5 of BarIconI
  ; ...
  (method (doit &tmp theTarget)
    (cond 
      ((not gSkipTarget)
        ; Don't do anything if no skip was set up.
        0
      )
      ((not (IsObject gSkipTarget))
        ; Skip target is a number, so a room.
        (gButtonBar disable: (ScriptID 0 8)) ; icon5 that is.
        (= theTarget gSkipTarget)
        (= gSkipTarget null)
        (gRoom newRoom: theTarget)
      )
      (else
        ; Skip target is something to cue.
        (gButtonBar disable: (ScriptID 0 8))
        (= theTarget gSkipTarget)
        (= gSkipTarget null)
        (theTarget cue:)
      )
    )
  )
)

As an example, here’s the ladder-climbing sequence with Merrily:

(instance rm260 of LarryRoom
  (properties
    picture 260
    horizon 11
  )
 
  (method (init)
    (super init: &rest)
    (= gSkipTarget gRoom)
    ((ScriptID 0 8) enable:)
    (self setScript: toTower)
    ; ...
  )
 
  (method (cue)
    ; Called when we click the Fast Forward button.
    ((gRoom script?) setScript: forwardScript)
  )
)
 
(instance forwardScript of Script
  (properties)
 
  (method (changeState newState &tmp oldCursor)
    (switchto (= state newState)
      (
        (= cycles 2)
      )
      (
        (= oldCursor gCursor)
        (gGame setCursor: 999)
        (SetCursor 225 87)
        (if
          (Print
            addTitle: "Just Not Into Rubber, Larry?"
            addText: "Do you really want to miss out on what promises to be a unique experience, Larry?"
            addButton: 0 "Oops" 0 35
            addButton: 1 "Yes" 155 35
            init:
          )
          (self cue:)
        else
          (gGame setCursor: oldCursor)
          ; Reset the skip and get rid of this script.
          (= gSkipTarget gRoom)
          (self dispose:)
        )
      )
      (
        ; We chose to skip. Change up our inventory...
        (gEgo get: 40 put: 35 put: 31 put: 20 put: 2)
        (= gSkipTarget null)
        ((ScriptID 0 8) disable:)
        (gGame handsOff: changeScore: 20 174 hideControls:)
        (= cycles 2)
      )
      (
        (SetPort 0)
        (SetPort 0 0 190 320 10 0)
        (Bset 8)
        (gSounds stop:)
        (DrawPic 98 dpOPEN_EDGECENTER) ; Black screen
        (gCast eachElementDo: #hide)
        (= cycles 2)
      )
      (
        (gRoom newRoom: 620) ; Go to your room
      )
    )
  )
)

And then there’s The Dating Pool. It has a simple skip system with a single global, like LSL6, but comes in two parts like LSL5 and FPFP.

(instance SkipIcon of cdIconItem
  (method (select)
    (if gSkip
      ; I *could* ask for confirmation here...
      (gIconBar hide:)
      (if (IsObject gSkip)
        (gSkip cue:)
      else
        (NewRoom gSkip)
      )
      (return true)
    )
  )
)
 
(procedure (SetSkip skip)
  (= gSkip (if argc skip else 0))
  (if gSkip
    ; Unlike LSL6's ButtonBar, an IconBar's IconItem doesn't have enable or disable methods.
    (SkipIcon signal: (| icHIDEBAR icRELEASE icIMMEDIATE))
  else
    ; I could let gIconBar enable or disable the icon but nyeh.
    (SkipIcon signal: (| icHIDEBAR icRELEASE icIMMEDIATE icDISABLED))
  )
)

And an example:

(instance IntroScript of Script
  (method (changeState newState)
    (switchto (= state newState)
      (
        (HandsOff)
        (SetSkip skipScript)
        ; ...
      )
      ; ...
    )
  )
)
 
(instance skipScript of Script
  (method (cue)
    (DrawPic 150 dpFADEOUT)
    ; Put us at place we'd be if we let the cutscene play out.
    (gEgo
      init:
      posn: 90 130
      resetCycler:
      view: 0
      loop: 2
    )
    (gRoom setScript: RoomScript)
  )
)

(Update: I’d changed the skip script in The Dating Pool to use cue instead of doit and allow gSkip to be a room number. And then I forgot to update the example.)

[ , , , ] Leave a Comment

On SCI Windows – Part 6 – Laura Bow, Frontier Pharmacist

It’s a double feature this time because these last two games’ windows are very similar.

These windows have decorations depending on your location or progress. Here are the view resources, both of them #994…

…and here is the code for the lb2Window first:

(instance lb2Win of SysWindow
  (properties
    ; This time, we set the custom bit up here.
    type 128
  )
 
  (method (open &tmp oldPort loop)
    ; Decide which decoration to show depending on our location.
    ; (These have been cut down a bit and the doubles were already
    ;  there. I don't know what's up with that.)
    (cond 
      ((OneOf gRoomNumber 280 210 330 240 260 300) (= loop 0))
      ((OneOf gRoomNumber 210 220 230 260 270 280) (= loop 1))
      ((OneOf gRoomNumber 100 105 110 120 140 150) (= loop 2))
      ((OneOf gRoomNumber 460 660 700 710 715 720) (= loop 4))
      ((OneOf gRoomNumber 335 340 350 355 360 370) (= loop 3))
      (else (= loop 4))
    )
 
    ; Make room for the edges, including the decorations.
    (= lsLeft (- left (/ (CelWide 994 loop 0) 2)))
    ; Yes, LB2 windows adjust for titles, despite the custom bit.
    (= lsTop (- top (if title 19 else 10)))
    (= lsRight (+ right (/ (CelWide 994 loop 0) 2)))
    ; Be at *least* as tall as the decoration.
    (= lsBottom (Max (+ bottom 3) (+ lsTop (CelHigh 994 loop 0) 3)))
 
    ; Always top priority
    (= priority 15)
    (super open:)
 
    ; Now we draw, on the whole screen.
    (= oldPort (GetPort))
    (SetPort 0)
 
    ; Draw our fill...
    (Graph grFILL_BOX top left bottom right 3 gBack 15)
    ; ...border...
    (Graph grDRAW_LINE (- top 1) (- left 1) (- top 1) right gFore 15)
    (Graph grDRAW_LINE (- top 1) (- left 1) bottom (- left 1) gFore 15)
    (Graph grDRAW_LINE bottom (- left 1) bottom right gFore 15)
    (Graph grDRAW_LINE (- top 1) right bottom right gFore 15)
    ; Show what we have wrought...
    (Graph grUPDATE_BOX top left bottom right 1)
    ; ...but also dirty the part where the decorations will go.
    (Graph grUPDATE_BOX lsTop lsLeft (+ lsTop (CelHigh 994 loop 0)) (+ lsLeft (CelWide 994 loop 0)) 1)
    (Graph grUPDATE_BOX lsTop (- lsRight (CelWide 994 loop 0))  (+ lsTop (CelHigh 994 loop 0)) lsRight 1)
    ; Why? I have no idea.
 
    ; Now draw the decorations!
    (DrawCel 994 loop 0 (+ lsLeft 1) (+ lsTop 1) -1)
    (DrawCel 994 loop 1 (- (- lsRight (CelWide 994 loop 0)) 1) (+ lsTop 1) -1)
 
    (SetPort oldPort)
  )
)

Don’t let the thicker right edge fool you — half that line is part of the decoration!

As I said, the code for fpWin is remarkably similar… yet different? Most of it’s just in how the thicker edges are drawn, but a significant bit is in the last part:

(instance fpWin of SysWindow
  (properties
    type 128
  )
 
  (method (open &tmp oldPort loop theLsTop theLsLeft theLsRight)
    ; Decide which decoration to show depending on the act.
    (switch gAct
      (1 (= loop 0))
      (2 (if (Bset 1) (= loop 2) else (= loop 1)))
      (3 (= loop 3))
      (4 (= loop 4))
      (5 (= loop 4))
    )
 
    ; Make room for the edges, including the decorations.
    (= lsLeft (- (- left 3) 15))
    (= lsTop (- (- top 3) (if title 25 else 15)))
    (= lsRight (+ right 3 15))
    (= lsBottom (Max (+ bottom 3) (+ lsTop (CelHigh 994 loop 0) 3)))
 
    ; Always top priority
    (= priority 15)
    (super open:)
 
    ; Now we draw, on the whole screen.
    (= oldPort (GetPort))
    (SetPort 0)
 
    ; Draw our fill...
    (Graph grFILL_BOX top left bottom right 3 gBack 15)
    (if title (= top (- top 10)))
 
    ; Inner box, middling dark...
    (Graph grDRAW_LINE (- top 1) (- left 1) (- top 1) right 17 15)
    (Graph grDRAW_LINE (- top 1) (- left 1) bottom (- left 1) 17 15)
    (Graph grDRAW_LINE bottom (- left 1) bottom right 17 15)
    (Graph grDRAW_LINE (- top 1) right bottom right 17 15)
    ; Middle box, lighter...
    (Graph grDRAW_LINE (- top 2) (- left 2) (- top 2) (+ right 1) 19 15)
    (Graph grDRAW_LINE (- top 2) (- left 2) (+ bottom 1) (- left 2) 19 15)
    (Graph grDRAW_LINE (+ bottom 1) (- left 2) (+ bottom 1) (+ right 1) 19 15)
    (Graph grDRAW_LINE (- top 2) (+ right 1) (+ bottom 1) (+ right 1) 19 15)
    ; And finally the outer, darkest box.
    (Graph grDRAW_LINE (- top 3) (- left 3) (- top 3) (+ right 2) 16 15)
    (Graph grDRAW_LINE (- top 3) (- left 3) (+ bottom 2) (- left 3) 16 15)
    (Graph grDRAW_LINE (+ bottom 2) (- left 3) (+ bottom 2) (+ right 2) 16 15)
    (Graph grDRAW_LINE (- top 3) (+ right 2) (+ bottom 2) (+ right 2) 16 15)
 
    ; Show what we have wrought.
    (Graph grUPDATE_BOX (- top 3) (- left 3) (+ bottom 3) (+ right 3) 1)
 
    ; Unlike LB2, FPFP does this part the hard way.
    (switch gAct
      (1
        (= theLsLeft (+ lsLeft 2))
        (= theLsRight (- (- lsRight 15) 14))
        (= theLsTop lsTop)
      )
      (2
        (if (Bset 1)
          (= theLsLeft lsLeft)
          (= theLsRight (- (- lsRight 15) 13))
          (= theLsTop (+ lsTop 2))
        else
          (= theLsLeft (+ lsLeft 8))
          (= theLsRight (- (- lsRight 15) 14))
          (= theLsTop (+ lsTop 4))
        )
      )
      (3
        (= theLsLeft (+ lsLeft 6))
        (= theLsRight (- (- lsRight 15) 40))
        (= theLsTop (+ lsTop 11))
      )
      (4
        (= theLsLeft (+ lsLeft 7))
        (= theLsRight (- (- lsRight 15) 14))
        (= theLsTop (+ lsTop 8))
      )
      (5
        (= theLsLeft (+ lsLeft 7))
        (= theLsRight (- (- lsRight 15) 14))
        (= theLsTop (+ lsTop 8))
      )
    )
 
    ; Now draw the decorations!
    (DrawCel 994 loop 0 theLsLeft theLsTop -1)
    (DrawCel 994 loop 1 theLsRight theLsTop -1)
 
    (SetPort oldPort)
  )
)

I don’t know why either. Maybe something silly about the decorations?

[ , , ] Leave a Comment