SCI/Specifications/SCI in action/Views and animation in SCI
Views and animation in SCI
Original document by Lars Skovlund with notes by Christoph Reichenbach
This chapter deals with a rather complex subject within SCI. The subsystem described here is one of the "bad boys" in SCI, since it calls functions in user space, as well as changing the value of various selectors. This document is not necessarily complete. There are several things I have not covered - because they are better off in a separate document, or simply because I haven't yet figured that part out. IOW, this stuff is incomplete. Things may change.
After drawing a pic on the screen (which is DrawPic's job, that doesn't surprise now, does it?), some views have to be added to it. There are two ways of doing this; the AddToPic and the Animate call. While AddToPic is used for static views, Animate lets each animated view in the cast list perform an "animation cycle".
An animation cycle is done entirely in SCI code (with the aid of some kernel calls). It involves two other objects; the mover and the cycler. The mover is responsible for controlling the motion of an actor towards a specific point, while the cycler changes the image of the actor, making him appear to walk, for instance.
The behaviour of a view is controlled by its signal property. This property contains a bitfield which describes a lot of animation-related stuff. The bits can be roughly divided into two groups; the script and interpreter bits (I had called them Option and State bits at first, but that is not entirely accurate). The first group allows the script to influence the drawing pro- cess somewhat, the other are used internally by the interpreter. The two groups overlap a bit, though.
Table 6-1. SCI and FreeSCI signal bits
|Bit #||Name||FreeSCI constant||Meaning|
|0||_K_VIEW_SIG_FLAG_STOP_UPDATE||A view updating process has ended|
|1||_K_VIEW_SIG_FLAG_UPDATED||The view object is being updated|
|2||noUpd||_K_VIEW_SIG_FLAG_NO_UPDATE||Don't actually draw the view|
|3||_K_VIEW_SIG_FLAG_HIDDEN||The view is hidden from sight. Often, if an actor is
supposed to enter and exit a room (such as the guards in the plazas in QfG2), this bit is used. When he's supposed to enter the room, bit 3 in his signal is cleared. When he leaves, bit 3 is set, but his SCI object is not deleted.
|4||fixPriOn||_K_VIEW_SIG_FLAG_FIX_PRI_ON||if this bit is set, the priority of the view never changes
(if it isn't, the interpreter recalculates the priority automagically).
|7||_K_VIEW_SIG_FLAG_REMOVE||The view should be removed from the screen (an interpreter
bit - its corresponding script bit is bit 3). If bit 3 isn't set as well, the view reappears on the next frame.
|8||_K_VIEW_SIG_FLAG_FROZEN||Deactivates the mover object of the view (it is "frozen" -
the view can still turn, however).
|10||_K_VIEW_SIG_FLAG_HIT_OBSTACLE||The view hit an obstacle on the last animation cycle|
|11||_K_VIEW_SIG_FLAG_DOESNT_TURN||Meaningful for actors only. Means that the actor does not
turn, even though he is walking the "wrong way".
|12||_K_VIEW_SIG_FLAG_NO_CYCLER||The view cycler is disabled. This makes the view float
instead of walk.
|14||ignrAct||_K_VIEW_SIG_FLAG_IGNORE_ACTOR||Actors can walk in the rectangle occupied by the view. The
behaviour of this bit is odd, and best expressed by example. The Guild Master in QfG1 has his bit 14 set. This means that ego (or someone else) can walk all the way to his chair (try sneaking in on him from behind). If we clear this bit, we can't sneak in on him.
|15||_K_VIEW_SIG_FLAG_DISPOSE_ME||The view should be disposed|
|||_K_VIEW_SIG_FLAG_FREESCI_PRIVATE||Used as an intermediate result by the interpreter; marks
views that are going to have their nsRect/lsRect regions redrawn (for the test in the main draw algorithm's step 17.1., below)
|||_K_VIEW_SIG_FLAG_FRESCI_STOPUPD||View has been 'stopupdated'. This flag is set whenever the
view has the STOP_UPDATE bit set, and cleared as soon as it moves again. Stopupdated views are collided against differently than normal views.
The unlisted bits are probably all interpreter bits. They don't seem to have an effect when set. Many bits seem to be involved in the decision whether to display a view or not. I have not completely figured this out.
Animate (see the Section called Kernel function 0x0b: Animate([DblList], [word]) in Chapter 5) can be called in two ways:
<syntax type="C"> Animate(DblList cast, bool cycle) Animate()</syntax>
If the second syntax is used, the two parameters are assumed to be zero.
The cast list is just a list of the views to draw. Animate creates a backup of this list for updating purposes. However, this backup cast list isn't just a normal copy. The interpreter copies some selectors from the view (view, loop, cel, nsRect) and places them in a special data structure. This indicates to me that there is a possibility that the view objects may be deleted even though an update is anticipated.
The general pseudocode for Animate goes as follows:
0. Backup PicNotValid: PicNotValid' := PicNotValid 1. If we don't have a new cast: 1.1. if PicNotValid is set: 1.1.1. Redraw picture with opening animation 1.2. exit 2. For each view in the cast list: 2.1. If view is not frozen: 2.1.1. call view::doit(), performing an animation cycle 3. Prepare a list of y coordinates by traversing the cast list 4. For each view in the cast list: 4.1. If the view resource view::view has not been loaded yet: 4.1.1. Load view.nr, where nr=view::view 5. For each view in the cast list: 5.1. If view::loop is invalid, set view::loop := 0 5.2. If view::cel is invalid, set view::cel := 0 6. Sort the cast list, first by y, then by z 7. For each view in the cast list: Update view::nsRect (SetNowSeen()) 8. For each view in the cast list: Unless the views' priority is fixed, recalculate it 9. For each view in the cast list: 9.1. If NO_UPDATE is set for the view: 9.1.1. If the following holds: 18.104.22.168. (VIEW_UPDATED || FORCE_UPDATE) 22.214.171.124. || (!(VIEW_UPDATED || FORCE_UPDATE) && !IS_HIDDEN && REMOVE_VIEW) 126.96.36.199. || (!(VIEW_UPDATED || FORCE_UPDATE) && !IS_HIDDEN && !REMOVE_VIEW && ALWAYS_UPDATE) 188.8.131.52. || (!(VIEW_UPDATED || FORCE_UPDATE) && IS_HIDDEN && ALWAYS_UPDATE) 184.108.40.206. then increase PicNotValid by one. 9.1.2. Clear the STOP_UPDATE flag 9.2. otherwise: 9.2.1. If (STOP_UPDATE and !ALWAYS_UPDATE) or (!STOP_UPDATE and ALWAYS_UPDATE) 220.127.116.11. Increase PicNotValid by one 9.2.2. Clear the FORCE_UPDATE flag 10. If PicNotValid is now greater than zero, call the sub-algorithm described separately 11. For each view: If NO_UPDATE, IS_HIDDEN and ALWAYS_UPDATE are not set: 11.1.  Save the area covered by the view's nsRect, store the handle in view::underBits 11.2.  Draw the view object 11.3.  If the view IS_HIDDEN, clear the REMOVE_VIEW bit (don't need to hide it twice) 11.4.  Insert the view into the backup cast list 16. If PicNotValid', our copy of the initial value of PicNotValid, is non-zero: 16.1. Refresh entire screen with opening animation 16.2. PicNotValid := 0 17. For each view in the cast list: 17.1.  If the view was changed in step 10 and neither REMOVE_VIEW nor NO_UPDATE is set: 17.1.1.  Redraw the nsRect and lsRect areas 17.1.2.  Copy the nsRect to the lsRect 17.1.3.  If IS_HIDDEN, set REMOVE_VIEW as well 22. For each view in the reverse cast list: 22.1.  If neither NO_UPDATE nor REMOVE_VIEW is set: 22.1.1. Restore the underbits 22.1.2. Clear the underbits 22.2.  if DISPOSE_ME is set, call view::dispose to dispose it
With the sub-algorithm being:
1. For each view from the cast list: 1.1.  If NO_UPDATE is set: 1.1.1.  If REMOVE_VIEW is set: 18.104.22.168. If PicNotValid is 0, restore the area covered by view::underBits 22.214.171.124. Free view::underBits 1.1.2.  Clear FORCE_UPDATE 1.1.3.  If VIEW_UPDATED is set: Clear VIEW_UPDATED and NO_UPDATE 1.2. otherwise (if NO_UPDATE is not set): 1.2.1. Clear STOP_UPDATE 1.2.2. Set NO_UPDATE 6. For each view from the cast list: 6.1.  Draw the view 6.2.  If ALWAYS_UPDATE, clear STOP_UPDATE, VIEW_UPDATED, NO_UPDATE, FORCE_UPDATE 6.3.  Clip the nsRect against the boundaries of the "natural" priority band of the view 6.4.  If IGNORE_ACTOR is clear, fill the area found in 6.3. with 0xf on the control map 11. For each view from the view list: 11.1. if NO_UPDATE is set: 11.1.1.  If IS_HIDDEN, then set REMOVE_VIEW, otherwise: 126.96.36.199. clear REMOVE_VIEW 188.8.131.52.  Save the area covered by the nsRect in the underBits 14. For each view from the cast list: 14.1. If NO_UPDATE is set and IS_HIDDEN is clear: 14.1.1.  Draw the view
Note that the ReAnimate subfunction (0x0D) of the Graph kernel call redraws parts of the maps using the cast list created by Animate, whereas the ShowBits call (0x0C) copies parts of the active map to the physical screen.
- This flag is used internally in FreeSCI; it can't be found in the view objects, only in their copies in the dynview widget list.
- The bit names I have written come from some debug information I got from QfG2 - type "suck blue frog" then Ctrl-W to save the cast list!