Created: around 09-07-2001
Last update: 10-07-2021
Back Previous Next

Beginners howto 3 - three ways to stop a rapidly cycling alarm


Cycling alarms - two or more alarms that go off one after the other continually - are most often used to make a doll blink. Other uses include: glinting jewellery, the smoke rising off a cigarette, or a scarf flapping in the wind. The principle is the same in each case: two or more cels mapped one after the other to suggest movement. For something like the flapping scarf, the alarms that map and unmap the cels would need to have 100-millisecond values to make the animation look smooth enough. That kind of timer activity can tax a processor, and you may actually hear the computer "grind" when such alarms begin to cycle - and they don't automatically turn themselves off if you switch to a page where the scarf isn't displayed. (NB. When I wrote this howto, the first Pentium computers were appearing on the market, and I owned a 486; but it still matters when you're running an old operating system in an emulator, and the KiSS viewers I grew up with need ancient OSes.) If you try to turn them off with the usual "timer(A, 0) timer(B, 0)" construction, you may find this doesn't work, because timer A managed to trigger alarm B just before it was stopped, and timer B called alarm A again just after it was stopped, and the scarf keeps flapping without missing a beat.

There are three solutions to this, where a flapping scarf can be changed to a hanging scarf (or a smoking cigarette to an extinguished one, or glinting jewellery to dull jewellery, etc). The initial code would look like this:

;@initialize()
;@  unmap(#4)         ; unmaps all four scarf cels
;@begin()             ; don't start alarms before begin()
;@  map("scarf1.cel") ; flapping scarf, cel 1
;@  timer(1,100)      ; starts the cycle

Then, instead of the usual code for permanently cycling alarms, which might look like this:

; three cycling alarms to alternate between three "flapping scarf" cels
;@alarm(1)
;@  unmap(#4) map("scarf2.cel") timer(2,100)
;@alarm(2)
;@  unmap(#4) map("scarf3.cel") timer(3,100)
;@alarm(3)
;@  unmap(#4) map("scarf1.cel") timer(1,100)

you can use one of the following:

Solution 1: the FKiSS3 way:

This is super-simple. FKiSS3 uses variables, which are called A to Z and all have a value of 0 when the set is opened. (There are also variables A0 to Z0, A1 to Z1 etc. but all you need is one variable which isn't used anywhere else.) In all three examples, you start and stop the scarf flapping by clicking on it. In this example, I'm going to use variable M. The code will look like this:

;@press(#4) ; press any cel of the scarf object
; Toggle value of M between 1 and 0
;@  ifgreaterthan(M,0)
;@    let(M,0) unmap(#4) map("scarf0.cel") ; unset variable, map hanging scarf
;@  else()
;@    let(M,1) timer(1,100) ; set variable, start timer
;@  endif()

;@alarm(1)
;@  ifequal(M,1)
;@    unmap(#4) map("scarf2.cel") timer(2,100)
;@  endif()
;@alarm(2)
;@  ifequal(M,1)
;@    unmap(#4) map("scarf3.cel") timer(3,100)
;@  endif()
;@alarm(3)
;@  ifequal(M,1)
;@    unmap(#4) map("scarf1.cel") timer(1,100)
;@  endif()

The alarms will refuse to do anything unless M = 1.

Solution 2: the FKiSS2.1 way

This solution uses ifmapped() and ifnotmapped(), which set timers depending on whether cels are mapped, and needs one extra alarm to clean up after the timers have been stopped. The cycling alarms are brought to a halt by mapping the cel of the scarf hanging down, and the code goes like this:

;@press(#4) ; press any cel of the scarf object
; Toggle hanging scarf mapped/unmapped
;@  altmap("scarf0.cel") ; if scarf changes to hanging, a timer is running.
;@  ifnotmapped("scarf0.cel",1,100) ; only set timer if scarf NOT hanging

;@alarm(1)
;@  unmap("scarf1.cel") map("scarf2.cel") ; first map cels
;@  ifmapped("scarf0.cel",4,1) ; if scarf hanging, go NOW to cleanup
;@  ifnotmapped("scarf0.cel",2,200) ; else, continue animation
;@alarm(2)
;@  unmap("scarf2.cel") map("scarf3.cel")
;@  ifmapped("scarf0.cel",4,1)
;@  ifnotmapped("scarf0.cel",3,200)
;@alarm(3)
;@  unmap("scarf3.cel") map("scarf1.cel")
;@  ifmapped("scarf0.cel", 4,1)
;@  ifnotmapped("scarf0.cel",1,200)
; Cleanup alarm: unmap the moving scarf parts
;@alarm(4)
;@  unmap("scarf1.cel") unmap("scarf2.cel") unmap("scarf3.cel")

As you can see, the code now unmaps cels instead of the whole object, because unmapping #4 would also unmap scarf0.cel, which is needed to start and stop the cycle of flapping alarms.

Solution 3: the FKiSS way

This is the solution most likely to work on any viewer, and it needs two extra alarms. The first extra alarm calls all the animation alarms one after another, and then itself; the second cleans up after the first. Because the real cycling alarm is now the first extra alarm - in this example, alarm 4 - the initial code becomes:

;@initialize()
;@  unmap(#4)         ; unmaps all four scarf cels
;@begin()             ; don't start alarms before begin()
;@  map("scarf1.cel") ; flapping scarf, cel 1
;@  timer(4,100)      ; starts the cycle with timer _4_, not 1
where alarm 4 replaces alarm 1 als the first in the animation sequence. The code to start and stop the animation is:
; stop the cycle by clicking on the flapping scarf
; this has to be repeated for every "animation" cel
;@press("scarf1.cel")
;@  timer(4, 0) timer(5,500)
;@press("scarf2.cel")
;@  timer(4, 0) timer(5,500)
;@press("scarf3.cel")
;@  timer(4, 0) timer(5,500)
(Note the 500-millisecond delay for alarm 5.)
; start the cycle by clicking on the hanging scarf, which sets alarm 4
;@press("scarf0.cel")
;@  unmap(#4) map("scarf1.cel") timer(4, 1)

; keep scarf flapping
; by setting three timers and then itself at 100 millisecond intervals
;@alarm(4)
;@  timer(1,100) timer(2,200) timer(3,300) timer(4,400)
;@alarm(1)
;@  unmap(#4) map("scarf2.cel")
;@alarm(2)
;@  unmap(#4) map("scarf3.cel")
;@alarm(3)
;@  unmap(#4) map("scarf1.cel")

; map hanging scarf when timers 1-4 are guaranteed 0
;@alarm(5)
;@  unmap(#4) map("scarf0.cel")

As there is only one cycling alarm, namely alarm 4 which repeats itself, the cycle is easy to stop, and after that it's a matter of letting the alarms called by alarm 4 execute themselves and then cleaning up with the second extra alarm which is guaranteed to go off after alarms 1 to 4 have finished. A refinement is to build the timer for alarm 5 into alarm 4 itself, so you don't have to remember to call alarm 5 under every event where alarm 4 is set to 0:

;@alarm(4)
;@  timer(1,100) timer(2,200) timer(3,300) timer(4,400) timer(5,500)

Since alarm 4 continually calls itself (and moves alarm 5 another 500 milliseconds forward) before alarm 5 can execute, alarm 5 can't be carried out until alarm 4 is stopped with "timer(4,0)".

BEWARE! There is one viewer (WKiSS 0.65, see WKiSS) which won't allow timers to be set to 0. They have to be set to their highest value, 32767, the idea being that this is so high that they won't go off.

Click on the image to download the demo set. demo set



Back Previous Next