Community
SCI Programming => SCI Syntax Help => Topic started by: MusicallyInspired on June 16, 2015, 12:31:27 PM
-
Is there a way to slow down a picture transition speed? Like a slow fadeout/fadein? I know it's possible because King's Quest 6 did it on the Sierra logo and title stone wall. Or is that maybe a function of palette transitioning rather than DrawPic?
-
You could always decompile the code and attempt to decipher the script for the game... All I have ever known or seen has been the style attribute of the public instance of Rm. It may be as simple as a different style number may represent a fast/ slow fade.
-
If it's not possible with transitions (I'm not sure), I believe you can fade from one palette to another, or darken the current palette until it's black or something. See if you can figure out how KQ6 did it, and if not, I can try to look around.
-
Right...I will check the source. Lol I'm sorry I should have thought of that.
-
Ok, there are some custom instances with vague variables that have something to do with palette transitioning. Can I get an explanation of what all the Palette and PalVary functions do and what their values represent? I'll try to look through the ScummVM code but that is...intimidating.
EDIT: From what I can guess by looking at game sources, the last three values of Palette are RGB values and the first value determines what to do with those RGB values? Am I close?
-
Have you looked at the sci.sh header?
(define palSET_FROM_RESOURCE 1)
(define palSET_FLAG 2)
(define palUNSET_FLAG 3)
(define palSET_INTENSITY 4)
(define palFIND_COLOR 5)
(define palANIMATE 6)
(define palSAVE 7)
(define palRESTORE 8)
(The decompiler should use these values for Palette calls. The original decompiled sources I provided wouldn't have those in them, but if you re-decompile they should appear).
Which ones do you have questions about? We can try to figure it out.
-
EDIT: From what I can guess by looking at game sources, the last three values of Palette are RGB values and the first value determines what to do with those RGB values? Am I close?
The meaning of the other parameters depends on the first value passed to Palette. This is what I've figured out from a quick glance at SQ5 and Scumm: (parameters in [] denote optional parameters)
Palette(palSET_INTENSITY fromIndex toIndex intensity [setPalette])
Palette(palFIND_COLOR red green blue) // Returns palette index of closest color?
Palette(palANIMATE fromIndex toIndex speed)
Palette(palSAVE) // Returns a handle to current saved palette?
Palette(palRestore handle) // Restores from handle
Palette(palSET_FROM_RESOURCE resourceNumber [force]) // 2 is "force", any other value is not force
Palette(palSET_FLAG fromIndex toIndex flags) // Not sure what flags are. SQ5 sets flag 10 in rm110. Why?
Palette(palUNSET_FLAG fromIndex toIndex flags)
It looks like PalVary is used for transitioning to a different palette over a certain number of steps, each step taking the specified number of ticks:
PalVary(pvINIT resourceNumber ticks [stepStop] [direction]) // stepStop, 64 by default. direction 1 by
default.
PalVary(pvUNINIT) // ends the palette transition
PalVary(pvGET_CURRENT_STEP) // returns which step we're currently at
PalVary(pvREVERSE resourceNumber [ticks] [stepStop] [direction]) // Same params as pvINIT, but looks like resourceNumber is ignored
PalVary(pvPAUSE_RESUME pause) // Presumably 1 for resume and 0 for pause? Uses a pause "stack".
PalVary(pvCHANGE_TARGET resourceNumber)
PalVary(pvCHANGE_TICKS ticks)
-
Yeah, I was looking at those. I decompiled a new source from KQ6 and was looking at the code for the Sierra logo which fades in and out slowly. There's an instance called FadeCode that uses local variables:
(instance FadeCode of Code
(properties)
(method (init param1 param2 param3)
= local3 0
(if (>= paramTotal 1)
= local2 param1
(if (>= paramTotal 2)
= local1 param2
(if (>= paramTotal 3)
= local3 param3
)
)
)
(send global78:add(self))
)
(method (doit)
(var temp0)
(if (<> local0 local2)
= local0 (+ local0 (* 1 local1))
Palette(palSET_INTENSITY 0 255 local0)
= temp0 0
(while (< temp0 10)
++temp0
)
)(else
(send global78:delete(self))
(if (local3 and IsObject(local3))
(send local3:cue())
= local3 0
)
)
)
)
It's used in the introScript's ChangeState method:
(method (changeState newState)
(var temp0[2])
(switch (= state newState)
(case 0
(send gKq6KeyDownHandler:add(self))
(if ((global169 and Platform(7)) and (> global87 0))
(self:setScript(winLogo self))
)(else
(self:setScript(dosLogo self))
)
)
(case 1
(send global2:drawPic(100 9))
= cycles 1
)
(case 2
(if (global169)
= cycles 1
)(else
(FadeCode:init(100 1 self))
)
)
(case 3
(six:init())
(send global102:
number(2)
loop(1)
play(self)
)
= seconds 5
)
(case 4
(six:setCycle(End))
)
(case 5
(send gKq6:handsOn())
= seconds 10
(send gKq6IconBar:
enable()
disable(0 1 2 3 4 5)
height(-100)
activateHeight(-100)
)
SetSynonyms(0 "ALEX")
(Cursor:showCursor(1))
(send gKq6:setCursor(gArrowCursor))
(six:stopUpd())
(openingBut:
init()
stopUpd()
)
(playBut:
init()
stopUpd()
)
(helpBut:
init()
stopUpd()
)
(restoreBut:
init()
stopUpd()
)
)
)
)
Picture 99 is the Sierra logo, Picture 100 is the King's Quest stone wall title screen. It looks like the Sierra logo is drawn first but in black, so a question I have is about this line of code in the room's init method:
(if ((== global87 0) or not global169)
Palette(palSET_INTENSITY 0 255 0)
Load(rsPIC 100)
(presents:init())
DrawPic(99)
)
Am I correct in assuming that "palSET_INTENSITY 0 255 0" is somehow drawing the pic (99) black with an intensity value of 0? What are all the values doing in this command anyway?
EDIT: Ah, you replied when I was typing. Thank you!
-
If you are talking about the Half Dome photo transforming to the Sierra logo at the beginning of the Windows KQ6 remember that that is the standalone AVI file, "HDLOGO.AVI".
-
I think he's talking about the code in script 100 of KQ6. The title screen room basically does:
- start with picture 99 (Sierra logo), and set intensity to zero
- then fade intensity to 100 (now we see the logo)
- then later, fade intensity back to 0 (now it's gone)
- then draw a new pic (the stone wall), which will be invisible because intensity == 0
- then fade intensity back to 100
It doesn't look like the transitions built into DrawPic have an adjustable speed. Not sure why!
-
Yeah, I'm aware the Windows version uses an AVI. That doesn't fade at all. I don't care about the Windows version. There are conditions in the script that call for the Windows logo or the DOS logo. In fact it's a simple:
(instance winLogo of Script
(properties)
(method (changeState newState)
(switch (= state newState)
(case 0
(send global2:drawPic(98))
= cycles 1
)
(case 1
(if (not ShowMovie(0 "hdlogo.avi"))
ShowMovie(1 0 5)
ShowMovie(2 0 self)
)(else
= cycles 1
)
)
(case 2
ShowMovie(6)
(self:dispose())
)
)
)
)
So I figured out how to fade slowly by copying some code from KQ6, however it crashed on me and complained it couldn't find "100.MSG", which is the title screen's "room" number as well. So I created a 100.MSG with a single line that said "?". Now for some reason after it draws the next pic after my logo (logo is 500.p56, next title screen is 501.p56) the "?" pops up forever and doesn't go away instead of the regular menu with "Play Game, Restore Game, etc".
Anyway, I've abandoned the slow fading backgrounds for now and decided to switch to another palette-cycling logo. I created one successfully but it was cycling the wrong way. So I tried doing "Palette(palANIMATE 191 128 -1)" instead of "Palette(palANIMATE 128 191 -1)" but it wouldn't cycle the other way it just froze. Can you not cycle backwards through a palette? I had to remake my image so that the gradient was going the other way, but that took a lot of work.
I did, however, discover that you CAN palette cycle AND slowfade a palette's intensity at the same time, which I was wondering about. I wonder if you can do a palette transition and a palette cycle at the same time as well. I assume you can. I'm excited at this prospect. SCI had a very versatile graphics engine!
-
So I figured out how to fade slowly by copying some code from KQ6, however it crashed on me and complained it couldn't find "100.MSG", which is the title screen's "room" number as well. So I created a 100.MSG with a single line that said "?". Now for some reason after it draws the next pic after my logo (logo is 500.p56, next title screen is 501.p56) the "?" pops up forever and doesn't go away instead of the regular menu with "Play Game, Restore Game, etc".
The "play game, restore game" etc.... are message resources in 0.msg. The template game's script 100 explicitly calls those (I'm not sure why they weren't in 100.msg - maybe I should move them in the template game, because that's kind of confusing - although it's possible other scripts also referenced them, so 0.msg made more sense). The code from KQ6 must expect things in 100.msg. Why not just copy the palette fading bits from KQ6? Seems like it would be less error-prone.
Anyway, I've abandoned the slow fading backgrounds for now and decided to switch to another palette-cycling logo. I created one successfully but it was cycling the wrong way. So I tried doing "Palette(palANIMATE 191 128 -1)" instead of "Palette(palANIMATE 128 191 -1)" but it wouldn't cycle the other way it just froze. Can you not cycle backwards through a palette? I had to remake my image so that the gradient was going the other way, but that took a lot of work.
The color indices need to stay in order (I guess). To reverse the direction just change the -1 to 1.
(And not that you need it now, but instead of remaking your image, couldn't you just reverse the gradient in the pic when you import the background for the first time? That would order the indices the opposite way)
I did, however, discover that you CAN palette cycle AND slowfade a palette's intensity at the same time, which I was wondering about. I wonder if you can do a palette transition and a palette cycle at the same time as well. I assume you can. I'm excited at this prospect. SCI had a very versatile graphics engine!
Cool!
I think an interesting little sample/tutorial project would be a small "game" where you can walk to different rooms to try out various bits of the SCI engine. For instance, there would be a palette room where interacting with various objects would cycle or fade palettes. And a sound room where you could play sounds/audio. A room with various types of conversations. A room where you can die in various ways. A room where you can trying out different types of object interactions (e.g. using inventory items on objects). A room where you can see how path-finding works with different polygons.
-
The "play game, restore game" etc.... are message resources in 0.msg. The template game's script 100 explicitly calls those (I'm not sure why they weren't in 100.msg - maybe I should move them in the template game, because that's kind of confusing - although it's possible other scripts also referenced them, so 0.msg made more sense). The code from KQ6 must expect things in 100.msg. Why not just copy the palette fading bits from KQ6? Seems like it would be less error-prone.
That's what I thought I did. I just copied the FadeCode instance and the code blocks that launch it. 100.MSG doesn't exist in the normal Template Game, though. I had to create it so it would stop complaining but then something launched it. The FadeCode instance does make a call to a global variable, which was a vague name but which I thought was gTheDoits because it was the same global variable number. Here's the instance again which calls it from KQ6:
(instance FadeCode of Code
(properties)
(method (init param1 param2 param3)
= local3 0
(if (>= paramTotal 1)
= local2 param1
(if (>= paramTotal 2)
= local1 param2
(if (>= paramTotal 3)
= local3 param3
)
)
)
(send global78:add(self))
)
(method (doit)
(var temp0)
(if (<> local0 local2)
= local0 (+ local0 (* 1 local1))
Palette(palSET_INTENSITY 0 255 local0)
= temp0 0
(while (< temp0 10)
++temp0
)
)(else
(send global78:delete(self))
(if (local3 and IsObject(local3))
(send local3:cue())
= local3 0
)
)
)
)
The color indices need to stay in order (I guess). To reverse the direction just change the -1 to 1.
Oh. Well that's simple.
(And not that you need it now, but instead of remaking your image, couldn't you just reverse the gradient in the pic when you import the background for the first time? That would order the indices the opposite way)
I tried that but it didn't work! Maybe I just did it wrong...
Cool!
I think an interesting little sample/tutorial project would be a small "game" where you can walk to different rooms to try out various bits of the SCI engine. For instance, there would be a palette room where interacting with various objects would cycle or fade palettes. And a sound room where you could play sounds/audio. A room with various types of conversations. A room where you can die in various ways. A room where you can trying out different types of object interactions (e.g. using inventory items on objects).
That is an idea I've been throwing around. Kind of like the Duke Nukem 3D tutorial maps for Sector Effectors/Sector Tags.
-
So I have the fading up and running... Here is what I have.
In it's own instance, the fade code script
(instance FadeCode of Code
(properties)
(method (init param1 param2 param3)
= local3 0
(if (>= paramTotal 1)
= local2 param1
(if (>= paramTotal 2)
= local1 param2
(if (>= paramTotal 3)
= local3 param3
)
)
)
(send gTheDoits:add(self))
)
(method (doit)
(var temp0)
(if (<> local0 local2)
= local0 (+ local0 (* 1 local1))
Palette(palSET_INTENSITY 0 255 local0)
= temp0 0
(while (< temp0 10)
++temp0
)
)(else
(send gTheDoits:delete(self))
(if (local3 and IsObject(local3))
(send local3:cue())
= local3 0
)
)
)
)
And of course because of the local variables, I declared those up by the uses
(local
local0
local1
local2
local3
)
I just used the template game sierra splash and just faded it in and out a couple of times. Declare the picture in the public instance of Rm. Also include the initial call to palette intensity which basically blacks it all out
Palette(palSET_INTENSITY 0 255 0)
Then in the roomScript's changestate method, I used this for my first few cases.
(method (changeState newState)
(var temp0, temp1[10],temp2)
(switch (= state newState)
(case 0
// fade in
(FadeCode:init(100 1 self))// intensityGoal incrementBy nextCase
)
(case 1
= seconds 5
)
(case 2
// fade out
(FadeCode:init(0 -1 self))// intensityGoal incrementBy nextCase
)
(case 3
(send gRoom:drawPic(110))
= seconds 5
)
(case 4
// fade in
(FadeCode:init(100 1 self))
)
...
-
Maybe give the local0...local3 meaningful names (e.g. currentIntensity, desiredIntensity, intensityIncrement, fadeDoneCallback)
-
Also, looking at this a bit more... I am pretty sure that the while loop in the fadeCode can be removed. It is in the doit method, but after the palette intensity has been changed. I would assume that this was intended to build in a bit more delay for a slower fade, but because of where it is, it is accomplishing nothing. I have done a bit of editing so that the while loop now does what I think it was supposed to be doing from the beginning
So First, I renamed the local variables per Phil's suggestion as well as included a new one which is going to be our delay time. The larger it is set, the slower the fade occurs.
(local
fCurrent
fTarget
fIncrement
fCallBack
fDelay
)
Then we have our fadeCode of Code instance.
(instance FadeCode of Code
(properties)
(method (init param1 param2 param3 param4)
= fCallBack 0
= fDelay 10
(if (>= paramTotal 1)
= fIncrement param1
(if (>= paramTotal 2)
= fTarget param2
(if (>= paramTotal 3)
= fCallBack param3
(if (>= paramTotal 4)
= fDelay param4
)
)
)
)
(send gTheDoits:add(self))
)
(method (doit)
(var temp0)
(while (< temp0 fDelay)
++temp0
)
(if(== temp0 fDelay)
(if (<> fCurrent fIncrement)
= fCurrent (+ fCurrent (* 1 fTarget))
Palette(palSET_INTENSITY 0 255 fCurrent)
= temp0 0
)
(else
(send gTheDoits:delete(self))
(if (fCallBack and IsObject(fCallBack))
(send fCallBack:cue())
= fCallBack 0
)
)
)// end if temp = fDelay
)
)
To make use of it, heres an example of fading in and out.
//FadeIn
(FadeCode:init(100 1 self 10000))// fTarget fIncrement fCallBack fDelay
//FadeOut
(FadeCode:init(0 -1 self 100)) // fTarget fIncrement fCallBack fDelay
And there you go, a variable speed fading screen
-
Nice!
Why don't you rename the parameters to something more meaningful than param#, e.g. pCallback?
-
Hmmm...how does one turn off a palette cycling animation? I've run into a problem where I'm drawing a new picture within the same room and the palette is still cycling but now it's a different palette and random colours are going crazy lol.
-
Simply by not calling Palette(palAnimate... ). You must be calling it in the doit method or something.
-
Like I said I'm in the same room, I'm just drawing a new picture in a new case of the room's changeState method. Can you not just stop cycling whenever you want or do you HAVE to switch rooms?
EDIT: Oh wait, it is in the doit method! Err...but if I put it anywhere else it doesn't animate.
I also still can't figure out why I can't draw a new picture (with DrawPic) without the dumb game looking for 100msg and displaying whatever's in it rather than the main title menu it's supposed to have. The only thing that triggers that is my extra code that draws a new picture. It doesn't make sense.
-
Just set a local variable to some value when you draw the new pic, and then make the Palette call conditionally on that local variable.
Or switch to a new Script for the room when you do the drawpic - one with a doit that doesn't cycle the palette.
Lots of ways to handle this.
The 100.msg not found thing is a bit baffling. The DrawPic kernel must call back into the game, and maybe the interpreter is calling into the wrong game code or something. I can repro it, let me take a look.
-
Ok, I figured it out. I guess the palette cycler has to be in the doit method to get triggered constantly or nothing happens. The variable idea worked great thank you.
I think I know why it keeps looking for 100.msg, though. It only happens when I click the mouse which means that it's look for a verb interaction for the room. How do you disable mouse verb interactions so that clicking on nothing doesn't try to do anything?
-
Ok, I think I know what's going on. Sort of.
First, it looks like this is caused by falling through to super:handleEvent in rm100:handleEvent.
Why does it do this? Look at the logic... if the current room state is 4, then it will call super. I guess in the normal case, an event will never be received while in state 4. (Although state 4 lasts for 5 cycles, so I don't see why an event couldn't come through if you clicked fast enough. Anyway, putting a DrawPic in case 4 has changed things. I'm not sure exactly how the interpreter handles events while a DrawPic transition is happening... but I guess events can come through? Hmm, no that doesn't really make sense. Maybe they are batched and get processed in the next frame immediately, and we're still in state 4.... anyway, I'm not positive.
[edit:] Ok, well if I increase the cycles from 5 to 20 or so, I start being able to repro the problem. I'm not sure why less than that makes it impossible to repro.
As for stopping the 100.msg from coming up, I was able to put an empty doVerb method in rm100:
(method (doVerb theEvent)
// don't call super!
)
I'm not sure if that's the "prescribed" way or not (maybe looking at title screens in other sierra games would clarify), but it seems to work.
-
Thanks! That'll do for now. Will look into it in the future.
-
As for stopping the 100.msg from coming up, I was able to put an empty doVerb method in rm100:
Set the claimed property of the event to TRUE.
-
I tried that (for evVERB), but it didn't work. And I didn't want to eat all mouse events. It looks like the manufactured evVERB event never comes through Rm's handleEvent? (I assumed it did because the Sierra code was testing for it).
There's something more going on here. Rgn's handleEvent just passes events of any type to doVerb if they're not claimed. That feels like something that shouldn't happen? (It looks like the "verb" event is created in IconBar::handleEvent, which must not be hit in this case (I guess because it's been disabled on the title screen)).
-
Regarding the palette cycling, I wonder if it would be less confusing if palANIMATE were called palSHIFT? (To give the idea that you need to keep calling it in order to animate).
-
Yeah, palSHIFT is a good idea. Maybe some variables for the shifting direction as well (palSHIFT_FORWARD for 1 and palSHIFT_BACKWARD for -1 or something). I can add those myself, I guess. Actually, now that I know it needs to be triggered every cycle I've got some ideas for oscillating palette shifts...in the right scenario that could be very effective.
Regarding the evVERB stuff, it'd be really nice if we had the debug mode activated for SCI1.1. I wonder, could we take the SQ5 beta, decompile it, and find the the magic scripts to enable debug mode? I'm not sure if debug mode is active in the SQ5 beta, though. I know it is in the SQ4 beta.
-
I assume you're talking about the debugger built in to the interpreter? That's... in the interpreter, not the scripts. AFAIK it was only in SCI0 games. Doesn't much matter anyway, since ScummVM has a much better debugger.
Or are you talking about the script debugger that lets you see visual, control, pri screens, and properties on the ego, etc...? It should be trivial to grab from somewhere else (doesn't the SCI0 template game have it? I forget). It's possible we could expand on it to make it more functional. It's not clear how it would help with verbs though.
-
Yes the one in the interpreter. I thought that there were magic script files that activates the debug mode in the interpreter...at least for some games. I know it's not in the scripts. Or maybe the script debugger is what they were talking about.
-
The magic files activated the (rather limited) script-based debug tool.
The internal debugger is activated by shift-shift-minus, or a SetDebug kernel call (both of which don't work (crash) in post SCI0 games). There's no "magic" that can enable this, because the debugger functionality is no longer in the interpreter.