File Coverage

blib/lib/Games/3D/Link.pm
Criterion Covered Total %
statement 75 95 78.9
branch 27 50 54.0
condition 6 12 50.0
subroutine 14 18 77.7
pod 12 12 100.0
total 134 187 71.6


line stmt bran cond sub pod time code
1              
2             # Link - link two objects together and allow sending signal(s) between them
3              
4             package Games::3D::Link;
5              
6             # (C) by Tels
7              
8 3     3   39601 use strict;
  3         7  
  3         164  
9              
10             require Exporter;
11 3         292 use Games::3D::Signal qw/
12             SIG_FLIP SIG_OFF SIG_DIE
13             SIG_ACTIVATE SIG_DEACTIVATE
14             signal_name
15 3     3   398 /;
  3         6  
16 3     3   918 use Games::3D::Thingy;
  3         7  
  3         148  
17 3     3   15 use vars qw/@ISA $VERSION/;
  3         5  
  3         3434  
18             @ISA = qw/Exporter Games::3D::Thingy/;
19              
20             $VERSION = '0.03';
21              
22             sub DEBUG () { 0; }
23              
24             ##############################################################################
25             # protected class vars
26              
27             {
28 0     0 1 0 sub add_timer { die ("You need to set a timer callback first.") };
29             my $timer = 'Games::3D::Link'; # make it point to our add_timer()
30             sub timer_provider
31             {
32 0 0   0 1 0 $timer = shift if @_ > 0;
33 0         0 $timer;
34             }
35             }
36              
37             ##############################################################################
38             # methods
39              
40             sub _init
41             {
42 3     3   5 my $self = shift;
43              
44 3         25 $self->SUPER::_init(@_);
45              
46 3         5 $self->{input_states} = {}; # for AND gates
47 3         11 $self->{inputs} = {};
48 3         8 $self->{outputs} = {};
49              
50 3         10 $self->{count} = 1; # send signal only once
51 3         6 $self->{delay} = 0; # immidiately
52 3         7 $self->{repeat} = 2000; # 2 seconds if count != 1
53 3         7 $self->{rand} = 0; # exactly
54 3         9 $self->{once} = 0; # not once
55 3         6 $self->{fixed_output} = undef; # none (just releay)
56 3         6 $self->{invert} = 0; # not
57 3         5 $self->{and} = 0; # act as OR gate
58 3         8 $self;
59             }
60              
61             # override signal() to be more complex than Thingy's default
62              
63             sub signal
64             {
65 9     9 1 48 my ($self,$input,$sig) = @_;
66              
67             # my $id = $input; $id = $input->{id} if ref($id);
68             # print "# ",$self->name()," received signal $sig from $id\n";
69              
70 9 50       23 die ("Unregistered input $input tried to send signal to link $self->{id}")
71             if !exists $self->{inputs}->{$input};
72              
73             # if the signal is DIE, DESTROY yourself
74 9 50       27 if ($sig == SIG_DIE)
75             {
76 0         0 $self->kill();
77 0         0 return;
78             }
79             # if the signal is ACTIVATE or DEACTIVATE, (in)activate yourself
80 9 50       24 if ($sig == SIG_ACTIVATE)
    100          
81             {
82 0         0 $self->activate();
83 0         0 return; # don't relay this signal
84             }
85             elsif ($sig == SIG_DEACTIVATE)
86             {
87 1         7 $self->deactivate();
88 1         4 return; # don't relay this signal
89             }
90              
91             # AND gate: all inputs must be in the same state to send the signal
92 8 50 66     19 if ($self->{and} && scalar keys %{$self->{inputs}} > 1)
  6         21  
93             {
94             # store the signal at the input (for AND gate)
95 0         0 $self->{input_states}->{$input} = $sig;
96             # and check the others
97 0         0 my $in = $self->{input_states};
98 0         0 foreach my $i (keys %$in)
99             {
100             # if not all match yet, don't send signal
101 0 0       0 return if ($in->{$i} != $sig);
102             }
103             }
104 8 100       24 return unless $self->{active} == 1; # inactive links don't send signals
105              
106             # if we need to always send the same signal, do so
107 7 100       22 if (defined $self->{fixed_output})
    50          
108             {
109 1         2 $sig = $self->{fixed_output};
110             }
111             # otherwise we might need to invert the signal to be sent
112             elsif ($self->{invert})
113             {
114 0         0 $sig = Games::3D::Signal::invert($sig); # invert()
115             }
116            
117             # need to delay sending, or send more than one time
118 7 50 33     30 if ($self->{count} != 1 || $self->{delay} != 0)
119             {
120             timer()->add_timer(
121             $self->{delay}, $self->{count}, $self->{repeat}, $self->{rand},
122             sub
123             {
124 0     0   0 $self->output($input,$sig);
125             },
126 0         0 );
127             }
128             else
129             {
130 7         36 print '# ',$self->name()," relays ",signal_name($sig),
131             " from $input to outputs.\n" if DEBUG;
132             # Send signal straight away.
133 7         27 $self->output($input,$sig); # send $sig to all outputs
134             }
135 7 50       37 $self->deactivate() if $self->{once};
136             }
137              
138             sub link
139             {
140 5     5 1 16 my ($self,$src,$dst) = @_;
141              
142 5         13 $self->{inputs}->{$src->{id}} = $src;
143 5 100 100     20 if ($self->{and} && scalar keys %{$self->{inputs}} > 1)
  3         24  
144             {
145 1         2 $self->{input_states}->{$src->{id}} = SIG_OFF;
146             }
147 5         11 $self->{outputs}->{$dst->{id}} = $dst;
148 5         22 $src->add_output($self); # the link appears as output
149 5         14 $dst->add_input($self); # and input at both ends
150             }
151              
152             sub unlink
153             {
154             # unlink all inputs and outputs from ourself
155 1     1 1 4 my $self = shift;
156              
157 1         9 $self->SUPER::unlink();
158              
159 1         2 $self->{input_states} = {};
160 1         2 $self;
161             }
162              
163             # override input() to also add the input state
164             sub add_input
165             {
166 0     0 1 0 my ($self,$src) = @_;
167              
168 0         0 $self->{inputs}->{$src->{id}} = $src;
169 0 0 0     0 if ($self->{and} && scalar keys %{$self->{inputs}} > 1)
  0         0  
170             {
171 0         0 $self->{input_states}->{$src->{id}} = SIG_OFF;
172             }
173 0         0 $self;
174             }
175              
176             sub delay
177             {
178             # Sets the initial delay of the link, the delay for each consecutive signal,
179             # and the randomized offset for these times.
180             # Note that the second delay only comes into play if the
181             # count() was set to a value different than 1, otherwise each firing of the
182             # link will use the first delay again.
183 1     1 1 2 my ($self,$delay,$rand,$repeat) = @_;
184              
185 1 50       4 $self->{delay} = abs($delay) if defined $delay;
186 1 50       3 $self->{repeat} = abs($repeat) if defined $repeat;
187 1 50       3 $self->{rand} = abs($rand) if defined $rand;
188 1         7 ($self->{delay},$self->{repeat},$self->{rand});
189             }
190              
191             sub count
192             {
193             # Sets the count. If != 1, the outgoing signal will be resent coun() times,
194             # each time delayed by a bit specified with delay(). A count of -1 means
195             # infinitely.
196              
197 4     4 1 9 my $self = shift;
198              
199 4 100       14 if (defined $_[0])
200             {
201 3         6 $self->{count} = shift;
202             }
203 4         15 $self->{count};
204             }
205            
206             sub once
207             {
208 1     1 1 2 my $self = shift;
209              
210 1 0       3 $self->{once} = ($_[0] ? 1 : 0) if @_ > 0;
    50          
211 1         4 $self->{once};
212             }
213              
214             sub invert
215             {
216 1     1 1 2 my $self = shift;
217              
218 1 0       3 $self->{invert} = $_[0] ? 1 : 0 if @_ > 0;
    50          
219 1         8 $self->{invert};
220             }
221              
222             sub fixed_output
223             {
224 1     1 1 2 my $self = shift;
225              
226 1 50       5 $self->{fixed_output} = shift if @_ > 0;
227 1         5 $self->{fixed_output};
228             }
229              
230             sub and_gate
231             {
232 3     3 1 6 my $self = shift;
233            
234 3 100       9 if (@_ > 0)
235             {
236 2 100       6 $self->{and} = $_[0] ? 1 : 0;
237             }
238 3         9 $self->{and};
239             }
240              
241             1;
242              
243             __END__