line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# $Id: Action.pm,v 1.5 2006/11/04 10:11:11 mike Exp $ |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Action.pm - an action in a Scott Adams game. |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Games::ScottAdams::Action; |
6
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
390
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub new { |
10
|
65
|
|
|
65
|
0
|
92
|
my $class = shift(); |
11
|
65
|
|
|
|
|
105
|
my($verb, $noun, $num) = @_; |
12
|
|
|
|
|
|
|
|
13
|
65
|
|
|
|
|
557
|
return bless { |
14
|
|
|
|
|
|
|
verb => $verb, |
15
|
|
|
|
|
|
|
noun => $noun, |
16
|
|
|
|
|
|
|
num => $num, # 0-based index into Game's list of actions |
17
|
|
|
|
|
|
|
### I don't think we actually use this |
18
|
|
|
|
|
|
|
comment => undef, # optional comment to be written through |
19
|
|
|
|
|
|
|
cond => [], # array of conditions to be satisfied |
20
|
|
|
|
|
|
|
res => [], # array of results to be executed |
21
|
|
|
|
|
|
|
}, $class; |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub verb { |
26
|
65
|
|
|
65
|
0
|
71
|
my $this = shift(); |
27
|
65
|
|
|
|
|
238
|
return $this->{verb}; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub noun { |
31
|
65
|
|
|
65
|
0
|
76
|
my $this = shift(); |
32
|
65
|
|
|
|
|
150
|
return $this->{noun}; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub comment { |
36
|
73
|
|
|
73
|
0
|
99
|
my $this = shift(); |
37
|
73
|
|
|
|
|
91
|
my($name) = @_; |
38
|
|
|
|
|
|
|
|
39
|
73
|
|
|
|
|
108
|
my $old = $this->{comment}; |
40
|
73
|
100
|
|
|
|
141
|
if (defined $name) { |
41
|
4
|
|
|
|
|
7
|
$this->{comment} = $name; |
42
|
|
|
|
|
|
|
} |
43
|
73
|
|
|
|
|
352
|
return $old; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# We'd like to compile these up front so we can complain about |
48
|
|
|
|
|
|
|
# unrecognised condition and actions while we still know where we are |
49
|
|
|
|
|
|
|
# in the source file. Unfortunately, we can't do it in general as the |
50
|
|
|
|
|
|
|
# action may refer to the names of rooms or items that have not yet |
51
|
|
|
|
|
|
|
# been defined. So all we can do at this stage is remember them for |
52
|
|
|
|
|
|
|
# later. |
53
|
|
|
|
|
|
|
# |
54
|
|
|
|
|
|
|
sub add_cond { |
55
|
59
|
|
|
59
|
0
|
70
|
my $this = shift(); |
56
|
59
|
|
|
|
|
69
|
my($text) = @_; |
57
|
|
|
|
|
|
|
|
58
|
59
|
|
|
|
|
113
|
push @{ $this->{cond} }, $text; |
|
59
|
|
|
|
|
315
|
|
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub add_result { |
63
|
121
|
|
|
121
|
0
|
134
|
my $this = shift(); |
64
|
121
|
|
|
|
|
137
|
my($text) = @_; |
65
|
|
|
|
|
|
|
|
66
|
121
|
|
|
|
|
132
|
push @{ $this->{res} }, $text; |
|
121
|
|
|
|
|
542
|
|
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# PRIVATE to the compile() method. |
71
|
149
|
|
|
149
|
0
|
373
|
sub ARG_NONE { 0 } # no argument |
72
|
131
|
|
|
131
|
0
|
398
|
sub ARG_NUM { 1 } # argument specifies a flag |
73
|
91
|
|
|
91
|
0
|
193
|
sub ARG_ROOM { 2 } # argument identifies a room |
74
|
82
|
|
|
82
|
0
|
169
|
sub ARG_ITEM { 3 } # argument identifies an item |
75
|
2
|
|
|
2
|
0
|
6
|
sub ARG_ITEMROOM { 4 } # arguments identify an item and a room |
76
|
2
|
|
|
2
|
0
|
5
|
sub ARG_ITEMITEM { 5 } # arguments identify two items |
77
|
|
|
|
|
|
|
|
78
|
1
|
|
|
1
|
|
6
|
use vars qw(%_cond %_res); # Global as they need to be visible to "sad" |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1541
|
|
79
|
|
|
|
|
|
|
%_cond = ( |
80
|
|
|
|
|
|
|
carried => [ 1, ARG_ITEM ], |
81
|
|
|
|
|
|
|
here => [ 2, ARG_ITEM ], |
82
|
|
|
|
|
|
|
accessible => [ 3, ARG_ITEM ], |
83
|
|
|
|
|
|
|
at => [ 4, ARG_ROOM ], |
84
|
|
|
|
|
|
|
'!here' => [ 5, ARG_ITEM ], |
85
|
|
|
|
|
|
|
'!carried' => [ 6, ARG_ITEM ], |
86
|
|
|
|
|
|
|
'!at' => [ 7, ARG_ROOM ], |
87
|
|
|
|
|
|
|
flag => [ 8, ARG_NUM ], |
88
|
|
|
|
|
|
|
'!flag' => [ 9, ARG_NUM ], |
89
|
|
|
|
|
|
|
loaded => [ 10, ARG_NONE ], |
90
|
|
|
|
|
|
|
'!loaded' => [ 11, ARG_NONE ], |
91
|
|
|
|
|
|
|
'!accessible' => [ 12, ARG_ITEM ], |
92
|
|
|
|
|
|
|
exists => [ 13, ARG_ITEM ], |
93
|
|
|
|
|
|
|
'!exists' => [ 14, ARG_ITEM ], |
94
|
|
|
|
|
|
|
counter_le => [ 15, ARG_NUM ], |
95
|
|
|
|
|
|
|
# counter_ge => [ 16, ARG_NUM ], |
96
|
|
|
|
|
|
|
counter_gt => [ 16, ARG_NUM ], |
97
|
|
|
|
|
|
|
# ### The documentation accompanying the scottfree |
98
|
|
|
|
|
|
|
# interpreter says that condition 16 tests for |
99
|
|
|
|
|
|
|
# current counter's value greater than or equal |
100
|
|
|
|
|
|
|
# to the argument, but inspection of the source |
101
|
|
|
|
|
|
|
# shows that it actually tests for strictly |
102
|
|
|
|
|
|
|
# greater-than. |
103
|
|
|
|
|
|
|
'!moved' => [ 17, ARG_ITEM ], |
104
|
|
|
|
|
|
|
moved => [ 18, ARG_ITEM ], |
105
|
|
|
|
|
|
|
counter_eq => [ 19, ARG_NUM ], |
106
|
|
|
|
|
|
|
); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
%_res = ( |
109
|
|
|
|
|
|
|
get => [ 52, ARG_ITEM ], |
110
|
|
|
|
|
|
|
drop => [ 53, ARG_ITEM ], |
111
|
|
|
|
|
|
|
moveto => [ 54, ARG_ROOM ], |
112
|
|
|
|
|
|
|
destroy => [ 55, ARG_ITEM ], |
113
|
|
|
|
|
|
|
set_dark => [ 56, ARG_NONE ], |
114
|
|
|
|
|
|
|
clear_dark => [ 57, ARG_NONE ], |
115
|
|
|
|
|
|
|
set_flag => [ 58, ARG_NUM ], |
116
|
|
|
|
|
|
|
destroy2 => [ 59, ARG_ITEM ], |
117
|
|
|
|
|
|
|
# Same as 55 in ScottFree |
118
|
|
|
|
|
|
|
clear_flag => [ 60, ARG_NUM ], |
119
|
|
|
|
|
|
|
die => [ 61, ARG_NONE ], |
120
|
|
|
|
|
|
|
put => [ 62, ARG_ITEMROOM ], |
121
|
|
|
|
|
|
|
game_over => [ 63, ARG_NONE ], |
122
|
|
|
|
|
|
|
look => [ 64, ARG_NONE ], |
123
|
|
|
|
|
|
|
score => [ 65, ARG_NONE ], |
124
|
|
|
|
|
|
|
inventory => [ 66, ARG_NONE ], |
125
|
|
|
|
|
|
|
set_0 => [ 67, ARG_NONE ], |
126
|
|
|
|
|
|
|
clear_0 => [ 68, ARG_NONE ], |
127
|
|
|
|
|
|
|
refill_lamp => [ 69, ARG_NONE ], ### UNTESTED |
128
|
|
|
|
|
|
|
clear_screen => [ 70, ARG_NONE ], ### UNTESTED |
129
|
|
|
|
|
|
|
save_game => [ 71, ARG_NONE ], |
130
|
|
|
|
|
|
|
swap => [ 72, ARG_ITEMITEM ], |
131
|
|
|
|
|
|
|
continue => [ 73, ARG_NONE ], ### UNTESTED |
132
|
|
|
|
|
|
|
# Automatic -- is there ever any need to use it explicitly? |
133
|
|
|
|
|
|
|
superget => [ 74, ARG_ITEM ], ### UNTESTED |
134
|
|
|
|
|
|
|
put_with => [ 75, ARG_ITEMITEM ], |
135
|
|
|
|
|
|
|
look2 => [ 76, ARG_NONE ], ### UNTESTED |
136
|
|
|
|
|
|
|
# Same as 64 in ScottFree |
137
|
|
|
|
|
|
|
decrease_counter => [ 77, ARG_NONE ], |
138
|
|
|
|
|
|
|
print_counter => [ 78, ARG_NONE ], |
139
|
|
|
|
|
|
|
set_counter => [ 79, ARG_NUM ], |
140
|
|
|
|
|
|
|
swap_loc_default => [ 80, ARG_NONE ], |
141
|
|
|
|
|
|
|
select_counter => [ 81, ARG_NUM ], ### UNTESTED |
142
|
|
|
|
|
|
|
# Current counter is swapped with specified backup counter |
143
|
|
|
|
|
|
|
add_counter => [ 82, ARG_NUM ], ### UNTESTED |
144
|
|
|
|
|
|
|
subtract_counter => [ 83, ARG_NUM ], ### UNTESTED |
145
|
|
|
|
|
|
|
print_noun => [ 84, ARG_NONE ], |
146
|
|
|
|
|
|
|
print_noun_nl => [ 85, ARG_NONE ], |
147
|
|
|
|
|
|
|
nl => [ 86, ARG_NONE ], |
148
|
|
|
|
|
|
|
swap_loc => [ 87, ARG_NUM ], |
149
|
|
|
|
|
|
|
pause => [ 88, ARG_NONE ], |
150
|
|
|
|
|
|
|
special => [ 89, ARG_NUM ], |
151
|
|
|
|
|
|
|
# This is special -- see ../../../../scottfree/Definition |
152
|
|
|
|
|
|
|
); |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub compile { |
156
|
65
|
|
|
65
|
0
|
76
|
my $this = shift(); |
157
|
65
|
|
|
|
|
75
|
my($game) = @_; |
158
|
|
|
|
|
|
|
|
159
|
65
|
|
|
|
|
109
|
my $verb = $game->resolve_verb($this->verb()); |
160
|
65
|
|
|
|
|
168
|
my $noun = $this->noun(); |
161
|
65
|
100
|
|
|
|
130
|
if ($verb == 0) { |
162
|
|
|
|
|
|
|
# This is a %occur, so the noun is a percentage probability |
163
|
18
|
50
|
|
|
|
37
|
$noun = 100 if !$noun; |
164
|
|
|
|
|
|
|
} else { |
165
|
47
|
|
|
|
|
125
|
$noun = $game->resolve_noun($noun); |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
65
|
|
|
|
|
122
|
my @condval = ( 150*$verb + $noun ); |
169
|
65
|
|
|
|
|
67
|
foreach my $cond (@{ $this->{cond} }) { |
|
65
|
|
|
|
|
149
|
|
170
|
59
|
|
|
|
|
137
|
my($opcode, $arg) = _lookup($game, $cond, 'condition', \%_cond); |
171
|
59
|
50
|
|
|
|
134
|
$arg = 0 if !defined $arg; |
172
|
59
|
|
|
|
|
150
|
push @condval, $opcode + 20*$arg; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
65
|
50
|
|
|
|
151
|
die "Oops! SA format doesn't support >5 conditions in an action" |
176
|
|
|
|
|
|
|
if @condval > 6; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Now gather results, with parameters going on the end of @condval |
179
|
|
|
|
|
|
|
#warn "handling results:\n" . join ('', map {"\t$_\n"} |
180
|
|
|
|
|
|
|
# @{ $this->{res} }); |
181
|
65
|
|
|
|
|
69
|
my @resval; |
182
|
65
|
|
|
|
|
66
|
foreach my $res (@{ $this->{res} }) { |
|
65
|
|
|
|
|
136
|
|
183
|
121
|
|
|
|
|
272
|
my($opcode, @arg) = _lookup($game, $res, 'result', \%_res); |
184
|
121
|
|
|
|
|
372
|
push @resval, [ $opcode, @arg ]; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# Right. This is slightly tricky. We now want to pack all the |
188
|
|
|
|
|
|
|
# results, together with their parameters, into as few action |
189
|
|
|
|
|
|
|
# octuplets as possible. We have four result slots available in |
190
|
|
|
|
|
|
|
# the first one, together with zero or more parameter slots |
191
|
|
|
|
|
|
|
# remaining in the condition area; thereafter, each action |
192
|
|
|
|
|
|
|
# octuplet offers four more result slots together with five |
193
|
|
|
|
|
|
|
# parameter slots in the condition area (which of course is one |
194
|
|
|
|
|
|
|
# more than we'll ever need.) |
195
|
65
|
|
|
|
|
89
|
my @conds; # list of completed octuplets |
196
|
65
|
|
|
|
|
85
|
my $argslot = @condval; # 0-based index within current octuplet |
197
|
65
|
|
|
|
|
76
|
my $resslot = 0; # 0-based index into "virtual array" |
198
|
65
|
|
|
|
|
146
|
push @condval, map { 0 } 1..(8-@condval); |
|
396
|
|
|
|
|
578
|
|
199
|
|
|
|
|
|
|
|
200
|
65
|
|
|
|
|
180
|
for (my $i = 0; $i < @resval; $i++) { |
201
|
121
|
|
|
|
|
173
|
my $res = $resval[$i]; |
202
|
121
|
|
|
|
|
187
|
my($opcode, @arg) = @$res; |
203
|
121
|
|
|
|
|
239
|
@arg = grep { defined } @arg; |
|
60
|
|
|
|
|
167
|
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
### Seems like 6 in next line should be 5. Think harder. |
206
|
121
|
100
|
33
|
|
|
697
|
if ($argslot + @arg > 6 || $resslot == 4 || |
|
|
|
100
|
|
|
|
|
|
|
|
33
|
|
|
|
|
207
|
|
|
|
|
|
|
($resslot == 3 && $i < @resval-1)) { |
208
|
|
|
|
|
|
|
# Current octuplet is full: skip to next |
209
|
6
|
|
|
|
|
11
|
my $cindex = 6 + int($resslot/2); |
210
|
6
|
50
|
|
|
|
15
|
$condval[$cindex] += |
211
|
|
|
|
|
|
|
($resslot % 2 == 0 ? 150 : 1) * 73; |
212
|
6
|
|
|
|
|
27
|
push @conds, join(' ', @condval); |
213
|
6
|
|
|
|
|
13
|
@condval = map { 0 } 1..8; |
|
48
|
|
|
|
|
74
|
|
214
|
6
|
|
|
|
|
9
|
$argslot = 1; # because slot 0 holds verb & noun |
215
|
6
|
|
|
|
|
9
|
$resslot = 0; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
121
|
|
|
|
|
206
|
my $cindex = 6 + int($resslot/2); |
219
|
121
|
100
|
|
|
|
246
|
$condval[$cindex] += |
220
|
|
|
|
|
|
|
($resslot % 2 == 0 ? 150 : 1) * $opcode; |
221
|
121
|
|
|
|
|
128
|
$resslot++; |
222
|
121
|
|
|
|
|
288
|
foreach my $arg (@arg) { |
223
|
60
|
50
|
|
|
|
112
|
if (!defined $arg) { |
224
|
0
|
|
|
|
|
0
|
print STDERR "", "arg in '@arg' (", scalar(@arg), ") undef\n"; |
225
|
|
|
|
|
|
|
} |
226
|
60
|
|
|
|
|
92
|
$condval[$argslot] = 20*$arg; |
227
|
60
|
|
|
|
|
282
|
$argslot++; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
65
|
|
|
|
|
304
|
push @conds, join(' ', @condval); |
232
|
|
|
|
|
|
|
#print STDERR "", "returning conds: ", join(' -- ', @conds), "\n"; |
233
|
65
|
|
|
|
|
328
|
return @conds; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub _lookup { |
238
|
180
|
|
|
180
|
|
260
|
my($game, $text, $caption, $href) = @_; |
239
|
|
|
|
|
|
|
|
240
|
180
|
|
|
|
|
304
|
$text =~ s/^\s+//; |
241
|
180
|
|
|
|
|
462
|
my($op, $arg) = split /\s+/, $text, 2; |
242
|
180
|
100
|
|
|
|
371
|
if ($op eq 'msg') { |
243
|
|
|
|
|
|
|
# This check is a hack, but does spot an otherwise subtle bug |
244
|
54
|
50
|
|
|
|
101
|
die "Oops! `msg' used as a condition (missing %result line?)" |
245
|
|
|
|
|
|
|
if $caption eq 'condition'; |
246
|
|
|
|
|
|
|
|
247
|
54
|
|
|
|
|
143
|
my $mnum = $game->resolve_message($arg); |
248
|
54
|
50
|
|
|
|
252
|
return ($mnum <= 51 ? $mnum : $mnum+50); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
126
|
|
|
|
|
187
|
my $ref = $href->{$op}; |
252
|
126
|
50
|
|
|
|
230
|
die "unrecognised $caption op '$op'" |
253
|
|
|
|
|
|
|
if !defined $ref; |
254
|
|
|
|
|
|
|
|
255
|
126
|
|
|
|
|
177
|
my($opcode, $argtype) = @$ref; |
256
|
126
|
100
|
|
|
|
205
|
if ($argtype == ARG_NONE) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
257
|
8
|
|
|
|
|
22
|
return ($opcode); |
258
|
|
|
|
|
|
|
} elsif ($argtype == ARG_NUM) { |
259
|
|
|
|
|
|
|
# Numeric argument already has the right numeric value. |
260
|
|
|
|
|
|
|
} elsif ($argtype == ARG_ROOM) { |
261
|
21
|
|
|
|
|
57
|
$arg = $game->resolve_room($arg, 'action'); |
262
|
|
|
|
|
|
|
} elsif ($argtype == ARG_ITEM) { |
263
|
66
|
|
|
|
|
174
|
$arg = $game->resolve_item($arg, 'action'); |
264
|
|
|
|
|
|
|
} elsif ($argtype == ARG_ITEMROOM) { |
265
|
1
|
|
|
|
|
3
|
my($arg1, $arg2) = split /\s+/, $arg, 2; |
266
|
1
|
|
|
|
|
4
|
$arg1 = $game->resolve_item($arg1, 'action'); |
267
|
1
|
|
|
|
|
4
|
$arg2 = $game->resolve_room($arg2, 'action'); |
268
|
1
|
|
|
|
|
10
|
return ($opcode, $arg1, $arg2); |
269
|
|
|
|
|
|
|
} elsif ($argtype == ARG_ITEMITEM) { |
270
|
0
|
|
|
|
|
0
|
my($arg1, $arg2) = split /\s+/, $arg, 2; |
271
|
0
|
|
|
|
|
0
|
$arg1 = $game->resolve_item($arg1, 'action'); |
272
|
0
|
|
|
|
|
0
|
$arg2 = $game->resolve_item($arg2, 'action'); |
273
|
0
|
|
|
|
|
0
|
return ($opcode, $arg1, $arg2); |
274
|
|
|
|
|
|
|
} else { |
275
|
0
|
|
|
|
|
0
|
die "unsupported argument type $argtype for op '$op'"; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
117
|
|
|
|
|
299
|
return ($opcode, $arg); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1; |