Restore game actions.
[kaya.git] / lib / clock.rb
blob272c1a98064a0b5d87a6070e4df5ac365810ce05
1 # Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 require 'observer_utils'
9 require 'toolkit'
12 # A clock keeps track of game time and sends regular updates to a graphical
13 # clock component whenever the displayed time needs to change.
15 # The clock is based on the idea of _displayed_ time, which is the value in
16 # milliseconds that is displayed to the user on a clock with a specified
17 # _resolution_. For example, if the remaining time is 13561 milliseconds, and
18 # the resolution is 1 second, the displayed time is 14000.
20 # In general, the displayed time is the smallest multiple of the resolution
21 # which is greater or equal to the actual time.
23 # The other important concept is the _step_. This is currently set to the
24 # constant 100, and represents the frequency with which the internal timer is
25 # fired.
27 # The resolution can be set while the clock is running, but only to a multiple
28 # of the step.
30 class Clock
31   include Observable
32   STEP = 100
33   
34   attr_reader :resolution
35   attr_accessor :debug
36   
37   #
38   # Create a clock object. Times are expressed in milliseconds.
39   # The timer_class argument can be used to provide a different factory for
40   # the internal timer (the default is Qt::Timer).
41   #
42   def initialize(main, increment, timer_class = Qt::Timer)
43     @rem = main
44     @rem_last = @rem
45     update_displayed
46     
47     @increment = increment
48     @resolution = 1000
49     @timer = timer_class.new
50     @timer.single_shot = true
51     @timer.on(:timeout) { tick }
52     @running = false
53     @time = Qt::Time.new
54     
55   end
56   
57   #
58   # Stop the timer.
59   #
60   # The clock keeps track of the portion of step elapsed since the last tick,
61   # and will compensate when resuming.
62   #
63   def stop
64     if @running
65       @timer.stop
66       
67       update_remaining
68       @rem += @increment
69       @displayed += @increment
70       
71       @running = false
72       fire :timer => timer
73     end
74   end
75   
76   #
77   # Start or resume the timer.
78   #
79   def start
80     if not @running
81       @rem_last = @rem
82       @time.start
84       @timer.start(delta)
85       @running = true
86     end
87   end
88   
89   #
90   # The current displayed time.
91   #
92   def timer
93     @displayed
94   end
95   
96   #
97   # The main internal method, called every STEP milliseconds to update the
98   # internal state and keep track of elapsed time.
99   #
100   # Fire timer signal when the displayed time is a multiple of resolution.
101   #
102   def tick
103     return unless @running
104     
105     update_remaining
106     @displayed -= STEP
107     
108     if @displayed % @resolution == 0
109       fire :timer => timer
110     end
111     
112     @timer.start(delta)
113   end
114   
115   #
116   # Forcibly set a time for this clock.
117   #
118   # As a side effect, this method causes the timer to stop.
119   #
120   def set_time(rem)
121     @rem = rem
122     update_displayed
123     
124     # always stop the timer when forcing a time
125     @timer.stop
126     @running = false
127     
128     fire :timer => timer
129   end
130   
131   #
132   # Whether the clock is running
133   #
134   def running?
135     @running
136   end
137   
138   #
139   # Set resolution in milliseconds.
140   #
141   # The value must be a multiple of STEP.
142   #
143   def resolution=(value)
144     if value <= 0 || (value % STEP != 0)
145       raise "Invalid resolution"
146     end
147     @resolution = value
148   end
149   
150   private
151   
152   def update_remaining
153     @rem = @rem_last - @time.elapsed
154   end
155   
156   def update_displayed
157     @displayed = @rem + STEP - 1 - (@rem - 1) % STEP
158   end
159   
160   def delta
161     [0, STEP - @displayed + @rem].max
162   end