scottkit 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/GPL-2 +339 -0
- data/Makefile +5 -0
- data/README +75 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/bin/scottkit +96 -0
- data/bin/scottkit.rb +96 -0
- data/data/.gitignore +1 -0
- data/data/adams/.gitignore +2 -0
- data/data/adams/AdamsGames.zip +0 -0
- data/data/adams/Makefile +18 -0
- data/data/crystal/crystal.map +112 -0
- data/data/crystal/crystal.sck +598 -0
- data/data/crystal/crystal.solution +82 -0
- data/data/dan-and-matt.sck +180 -0
- data/data/dan-and-matt.solution +32 -0
- data/data/howarth/.gitignore +2 -0
- data/data/howarth/Makefile +14 -0
- data/data/howarth/mysterious.tar.gz +0 -0
- data/data/test/Makefile +18 -0
- data/data/test/adams/Makefile +13 -0
- data/data/test/adams/adv01.solution +186 -0
- data/data/test/adams/adv01.transcript +869 -0
- data/data/test/adams/adv01.transcript.md5 +1 -0
- data/data/test/adams/adv02.solution +225 -0
- data/data/test/adams/adv02.transcript +970 -0
- data/data/test/adams/adv02.transcript.md5 +1 -0
- data/data/test/adams/adv04.solution +187 -0
- data/data/test/adams/adv04.transcript +876 -0
- data/data/test/adams/adv04.transcript.md5 +1 -0
- data/data/test/crystal.decompile +628 -0
- data/data/test/crystal.sao +373 -0
- data/data/test/crystal.save-file +62 -0
- data/data/test/crystal.save-script +41 -0
- data/data/test/crystal.sck +598 -0
- data/data/test/crystal.solution +82 -0
- data/data/test/crystal.transcript +413 -0
- data/data/test/t6.pretty-print +225 -0
- data/data/test/t7.sao +110 -0
- data/data/test/t7.solution +28 -0
- data/data/test/t7.transcript +147 -0
- data/data/tutorial/t1.sck +5 -0
- data/data/tutorial/t2.sck +14 -0
- data/data/tutorial/t3.sck +38 -0
- data/data/tutorial/t4.sck +62 -0
- data/data/tutorial/t5.sck +87 -0
- data/data/tutorial/t6.sck +119 -0
- data/data/tutorial/t7.sck +135 -0
- data/lib/scottkit/compile.rb +661 -0
- data/lib/scottkit/decompile.rb +175 -0
- data/lib/scottkit/game.rb +409 -0
- data/lib/scottkit/play.rb +474 -0
- data/notes/Definition +147 -0
- data/notes/Definition.saved-game +9 -0
- data/notes/Definition.scottfree-1.14 +142 -0
- data/notes/adventureland-maze +20 -0
- data/notes/continue-action +51 -0
- data/test/test_canonicalise.rb +94 -0
- data/test/test_compile.rb +54 -0
- data/test/test_decompile.rb +13 -0
- data/test/test_play.rb +20 -0
- data/test/test_playadams.rb +36 -0
- data/test/test_save.rb +31 -0
- data/test/withio.rb +15 -0
- data/test/withio_test.rb +31 -0
- metadata +118 -0
@@ -0,0 +1,474 @@
|
|
1
|
+
module ScottKit
|
2
|
+
class Game
|
3
|
+
private
|
4
|
+
|
5
|
+
# Returns 1 if the game was won, 0 otherwise
|
6
|
+
def play
|
7
|
+
@finished = nil
|
8
|
+
@items.each { |x| x.loc = x.startloc }
|
9
|
+
@flags = Array.new(NFLAGS) { false } # weird way to set a default
|
10
|
+
@counters = Array.new(NFLAGS) { 0 }
|
11
|
+
@saved_rooms = Array.new(NFLAGS) { 0 }
|
12
|
+
@counter = 0
|
13
|
+
@saved_room = 0
|
14
|
+
@loc = defined?(@startloc) ? @startloc : 1
|
15
|
+
@lampleft = defined?(@lamptime) ? @lamptime : 0
|
16
|
+
dark_flag = false
|
17
|
+
|
18
|
+
puts "ScottKit, a Scott Adams game toolkit in Ruby."
|
19
|
+
puts "Release 1.0, (C) 2010 Mike Taylor <mike@miketaylor.org.uk>"
|
20
|
+
puts "Distributed under the GNU software license"
|
21
|
+
|
22
|
+
if file = options[:restore_file]
|
23
|
+
restore(file)
|
24
|
+
puts "Restored saved game #{file}"
|
25
|
+
end
|
26
|
+
|
27
|
+
if seed = options[:random_seed]
|
28
|
+
puts "Setting random seed #{seed}"
|
29
|
+
srand(seed)
|
30
|
+
end
|
31
|
+
|
32
|
+
@fh = nil
|
33
|
+
if file = options[:read_file]
|
34
|
+
@fh = File.new(file)
|
35
|
+
raise "#$0: can't read input file '#{file}': #$!" if !@fh
|
36
|
+
end
|
37
|
+
|
38
|
+
look
|
39
|
+
while true
|
40
|
+
run_matching_actions(0, 0)
|
41
|
+
return @finished != 0 if @finished
|
42
|
+
print "Tell me what to do ? "
|
43
|
+
if !(line = gets)
|
44
|
+
puts
|
45
|
+
break
|
46
|
+
end
|
47
|
+
words = line.chomp.split
|
48
|
+
execute_command(words[0], words[1])
|
49
|
+
if items.size > ITEM_LAMP &&
|
50
|
+
items[ITEM_LAMP].loc != ROOM_NOWHERE && @lampleft > 0
|
51
|
+
@lampleft -= 1
|
52
|
+
if @lampleft == 0
|
53
|
+
puts "Your light has run out"
|
54
|
+
@flags[FLAG_LAMPDEAD] = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
0
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get a line from @fh if defined, otherwise $stdin
|
62
|
+
def gets
|
63
|
+
line = nil
|
64
|
+
if (@fh)
|
65
|
+
line = @fh.gets
|
66
|
+
@fh = nil if !line
|
67
|
+
end
|
68
|
+
line = $stdin.gets if !line
|
69
|
+
return nil if !line
|
70
|
+
puts line if @fh || options[:echo_input]
|
71
|
+
line
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns index of word in list, or nil if not in vocab
|
75
|
+
# Word may be undefined, in which case 0 is returned
|
76
|
+
def findword(word, list)
|
77
|
+
return 0 if !word
|
78
|
+
word = (word || "").upcase[0, @wordlen]
|
79
|
+
list.each.with_index do |junk, index|
|
80
|
+
target = list[index].upcase
|
81
|
+
if word == target[0, @wordlen]
|
82
|
+
return index
|
83
|
+
elsif target[0] == "*" && word == target[1, @wordlen+1]
|
84
|
+
while list[index][0] == "*"
|
85
|
+
index -= 1
|
86
|
+
end
|
87
|
+
return index
|
88
|
+
end
|
89
|
+
end
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def execute_command(verb, noun)
|
94
|
+
if (options[:wizard_mode] && verb)
|
95
|
+
if wizard_command(verb, noun)
|
96
|
+
return
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
verb = "inventory" if verb == "i"
|
101
|
+
verb = "look" if verb == "l"
|
102
|
+
@noun = noun
|
103
|
+
vindex = findword(verb, verbs)
|
104
|
+
nindex = findword(noun, nouns)
|
105
|
+
if !vindex && !noun
|
106
|
+
if tmp = findword(verb, nouns) || findword(verb, %w{XXX n s e w u d})
|
107
|
+
vindex, nindex = VERB_GO, tmp
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
if !vindex || !nindex
|
112
|
+
puts "You use word(s) I don't know!"
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
dputs :show_parse, "vindex=#{vindex}, nindex=#{nindex}"
|
117
|
+
case run_matching_actions(vindex, nindex)
|
118
|
+
when :success then return
|
119
|
+
when :failconds then recognised_command = true
|
120
|
+
when :nomatch then recognised_command = false
|
121
|
+
end
|
122
|
+
|
123
|
+
# Automatic GO
|
124
|
+
1.upto 6 do |i|
|
125
|
+
if vindex == VERB_GO && nindex == i
|
126
|
+
puts "Dangerous to move in the dark!" if is_dark
|
127
|
+
newloc = @rooms[@loc].exits[i-1]
|
128
|
+
if newloc != 0
|
129
|
+
@loc = newloc
|
130
|
+
look
|
131
|
+
# When moving from a light place to a dark one or vice
|
132
|
+
# versa, this look should not happen, because we'll get a
|
133
|
+
# correct version of the display after to flag gets set.
|
134
|
+
# I'm not sure what can be done about this. ### Think
|
135
|
+
elsif is_dark
|
136
|
+
puts "I fell down and broke my neck."
|
137
|
+
finished(0)
|
138
|
+
else
|
139
|
+
puts "I can't go in that direction."
|
140
|
+
end
|
141
|
+
return
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Automatic GET/DROP
|
146
|
+
if (vindex == VERB_GET)
|
147
|
+
return autoget(nindex)
|
148
|
+
elsif (vindex == VERB_DROP)
|
149
|
+
return autodrop(nindex)
|
150
|
+
end
|
151
|
+
|
152
|
+
if (recognised_command)
|
153
|
+
puts "I can't do that yet."
|
154
|
+
else
|
155
|
+
puts "I don't understand your command."
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def run_matching_actions(vindex, nindex)
|
160
|
+
recognised_command = false
|
161
|
+
@actions.each_index do |i|
|
162
|
+
action = @actions[i]
|
163
|
+
if vindex == action.verb &&
|
164
|
+
(vindex == 0 || (nindex == action.noun || action.noun == 0))
|
165
|
+
recognised_command = true
|
166
|
+
case action.execute(vindex == 0)
|
167
|
+
when :failconds
|
168
|
+
# Do nothing
|
169
|
+
when :success
|
170
|
+
return :success if vindex != 0
|
171
|
+
when :continue
|
172
|
+
while true
|
173
|
+
action = @actions[i += 1]
|
174
|
+
break if !action || action.verb != 0 || action.noun != 0
|
175
|
+
action.execute(false)
|
176
|
+
end
|
177
|
+
return :success if vindex != 0
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
return recognised_command ? :failconds : :nomatch
|
182
|
+
end
|
183
|
+
|
184
|
+
def wizard_command(verb, noun)
|
185
|
+
optnames = {
|
186
|
+
"c" => :show_conditions,
|
187
|
+
"i" => :show_instructions,
|
188
|
+
"r" => :show_random,
|
189
|
+
"p" => :show_parse,
|
190
|
+
}
|
191
|
+
|
192
|
+
if verb.upcase == "#SG" # superget
|
193
|
+
i = Integer(noun)
|
194
|
+
if (i < 0 || i > @items.count)
|
195
|
+
puts "#{i} out of range 0..#{@items.count}"
|
196
|
+
else
|
197
|
+
@items[i].loc = ROOM_CARRIED
|
198
|
+
puts "Supergot #{@items[i].desc}"
|
199
|
+
end
|
200
|
+
elsif verb.upcase == "#GO" # teleport
|
201
|
+
i = Integer(noun)
|
202
|
+
if (i < 0 || i > @rooms.count)
|
203
|
+
puts "#{i} out of range 0..#{@rooms.count}"
|
204
|
+
else
|
205
|
+
@loc = i
|
206
|
+
look
|
207
|
+
end
|
208
|
+
elsif verb.upcase == "#WHERE" # find an item
|
209
|
+
i = Integer(noun)
|
210
|
+
if (i < 0 || i > @items.count)
|
211
|
+
puts "#{i} out of range 0..#{@items.count}"
|
212
|
+
else
|
213
|
+
item = @items[i]
|
214
|
+
loc = item.loc
|
215
|
+
puts "#Item #{i} (#{item.desc}) at room #{loc} (#{rooms[loc].desc})"
|
216
|
+
end
|
217
|
+
elsif verb.upcase == "#SET"
|
218
|
+
if (sym = optnames[noun])
|
219
|
+
@options[sym] = true
|
220
|
+
else
|
221
|
+
puts "Option '#{noun}' unknown"
|
222
|
+
end
|
223
|
+
elsif verb.upcase == "#CLEAR"
|
224
|
+
if (sym = optnames[noun])
|
225
|
+
@options[sym] = false
|
226
|
+
else
|
227
|
+
puts "Option '#{noun}' unknown"
|
228
|
+
end
|
229
|
+
else
|
230
|
+
return false
|
231
|
+
end
|
232
|
+
true
|
233
|
+
end
|
234
|
+
|
235
|
+
def autoget(nindex)
|
236
|
+
return puts "What ?" if nindex == 0
|
237
|
+
noun = @nouns[nindex].upcase[0, @wordlen]
|
238
|
+
if !(item = @items.find { |x| x.name == noun && x.loc == @loc })
|
239
|
+
puts "It's beyond my power to do that."
|
240
|
+
elsif ncarried == @maxload
|
241
|
+
puts "I've too much to carry!"
|
242
|
+
else
|
243
|
+
item.loc = ROOM_CARRIED
|
244
|
+
puts "O.K."
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def autodrop(nindex)
|
249
|
+
return puts "What ?" if nindex == 0
|
250
|
+
noun = @nouns[nindex].upcase[0, @wordlen]
|
251
|
+
if !(item = @items.find { |x| x.name == noun && x.loc == ROOM_CARRIED })
|
252
|
+
puts "It's beyond my power to do that."
|
253
|
+
else
|
254
|
+
item.loc = @loc
|
255
|
+
puts "O.K."
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def look #:nodoc:
|
260
|
+
puts
|
261
|
+
if is_dark
|
262
|
+
return print "I can't see. It is too dark!\n\n"
|
263
|
+
end
|
264
|
+
|
265
|
+
room = @rooms[@loc]
|
266
|
+
s = room.desc
|
267
|
+
if s =~ /^\*/
|
268
|
+
puts s.sub(/^\*/, "")
|
269
|
+
else
|
270
|
+
puts "I'm in a #{s}"
|
271
|
+
end
|
272
|
+
if (room.exits.find { |x| x != 0 })
|
273
|
+
print "Obvious exits: "
|
274
|
+
puts room.exits.each.with_index.map { |x, i| [ i, x ] }.
|
275
|
+
select { |x| x[1] != 0 }.
|
276
|
+
map { |x| dirname(x[0]).capitalize }.join(", ") + "."
|
277
|
+
end
|
278
|
+
if @items.find { |x| x.loc == @loc }
|
279
|
+
print "I can also see: "
|
280
|
+
puts @items.select { |x| x.loc == @loc }.
|
281
|
+
map { |x| x.desc.gsub('`', '"') }.join(", ")
|
282
|
+
end
|
283
|
+
puts
|
284
|
+
end
|
285
|
+
|
286
|
+
def inventory #:nodoc:
|
287
|
+
puts "I'm carrying:"
|
288
|
+
carried = items.select { |x| x.loc == ROOM_CARRIED }.map { |x| x.desc }
|
289
|
+
puts((carried.size == 0 ? "Nothing" : carried.join(" - ")) + ".")
|
290
|
+
end
|
291
|
+
|
292
|
+
def score #:nodoc:
|
293
|
+
count = @items.select { |item|
|
294
|
+
item.desc[0] == "*" && item.loc == @treasury
|
295
|
+
}.count
|
296
|
+
print "I've stored #{count} treasures. "
|
297
|
+
puts "On a scale of 0 to 100, that rates #{100*count/@ntreasures}."
|
298
|
+
if (count == @ntreasures)
|
299
|
+
puts "Well done."
|
300
|
+
finished(1)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def prompt_and_save #:nodoc:
|
305
|
+
print "Filename: "
|
306
|
+
name = gets || return
|
307
|
+
name.chomp!
|
308
|
+
save(name)
|
309
|
+
end
|
310
|
+
|
311
|
+
def finished(win) #:nodoc:
|
312
|
+
puts "The game is now over."
|
313
|
+
sleep 2 if !options[:no_wait]
|
314
|
+
@finished = win
|
315
|
+
end
|
316
|
+
|
317
|
+
def ncarried #:nodoc:
|
318
|
+
items.select { |x| x.loc == ROOM_CARRIED }.size
|
319
|
+
end
|
320
|
+
|
321
|
+
def is_dark
|
322
|
+
return dark_flag if @items.size <= ITEM_LAMP
|
323
|
+
loc = @items[ITEM_LAMP].loc
|
324
|
+
#puts "dark_flag=#{dark_flag}, lamp(#{ITEM_LAMP}) at #{loc}"
|
325
|
+
dark_flag && loc != ROOM_CARRIED && loc != @loc
|
326
|
+
end
|
327
|
+
|
328
|
+
public :play # Must be visible to driver program
|
329
|
+
public :prompt_and_save, :look, :score, :ncarried, :inventory, :finished # Invoked from Instruction.execute()
|
330
|
+
|
331
|
+
|
332
|
+
class Condition
|
333
|
+
def evaluate
|
334
|
+
loc = @game.loc
|
335
|
+
item = @game.items[@value]
|
336
|
+
case @cond
|
337
|
+
when 0 then raise "unexpected condition code 0"
|
338
|
+
when 1 then item.loc == ROOM_CARRIED
|
339
|
+
when 2 then item.loc == loc
|
340
|
+
when 3 then item.loc == ROOM_CARRIED || item.loc == loc
|
341
|
+
when 4 then loc == @value
|
342
|
+
when 5 then item.loc != loc
|
343
|
+
when 6 then item.loc != ROOM_CARRIED
|
344
|
+
when 7 then loc != @value
|
345
|
+
when 8 then @game.flags[@value]
|
346
|
+
when 9 then !@game.flags[@value]
|
347
|
+
when 10 then @game.ncarried != 0
|
348
|
+
when 11 then @game.ncarried == 0
|
349
|
+
when 12 then item.loc != ROOM_CARRIED && item.loc != loc
|
350
|
+
when 13 then item.loc != ROOM_NOWHERE
|
351
|
+
when 14 then item.loc == ROOM_NOWHERE
|
352
|
+
when 15 then @game.counter <= @value
|
353
|
+
when 16 then @game.counter > @value
|
354
|
+
when 17 then item.loc == item.startloc
|
355
|
+
when 18 then item.loc != item.startloc
|
356
|
+
# From the description, it seems that perhaps "moved" should
|
357
|
+
# be true if the object has EVER been moved; but the ScottFree
|
358
|
+
# code implements it as I have done here.
|
359
|
+
when 19 then @game.counter == @value
|
360
|
+
else raise "unimplemented condition code #@cond"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
class Instruction
|
367
|
+
# Returns true iff interpreter should continue to next action
|
368
|
+
def execute(args)
|
369
|
+
@game.dputs :show_instructions,
|
370
|
+
" executing #{self.render(args.clone)}"
|
371
|
+
if (@op == 0)
|
372
|
+
return false # shouldn't happen
|
373
|
+
elsif (@op <= 51)
|
374
|
+
puts @game.messages[@op].gsub('`', '"')
|
375
|
+
return false
|
376
|
+
elsif (@op >= 102)
|
377
|
+
puts @game.messages[@op-50].gsub('`', '"')
|
378
|
+
return false
|
379
|
+
else case @op
|
380
|
+
when 52 then
|
381
|
+
if @game.ncarried == @game.maxload
|
382
|
+
puts "I've too much to carry!"
|
383
|
+
else
|
384
|
+
@game.items[args.shift].loc = ROOM_CARRIED
|
385
|
+
puts "O.K."
|
386
|
+
end
|
387
|
+
when 53 then @game.items[args.shift].loc = @game.loc
|
388
|
+
when 54 then @game.loc = args.shift
|
389
|
+
when 55 then @game.items[args.shift].loc = ROOM_NOWHERE
|
390
|
+
when 56 then @game.dark_flag = true
|
391
|
+
when 57 then @game.dark_flag = false
|
392
|
+
when 58 then @game.flags[args.shift] = true
|
393
|
+
when 59 then @game.items[args.shift].loc = ROOM_NOWHERE
|
394
|
+
when 60 then @game.flags[args.shift] = false
|
395
|
+
when 61 then
|
396
|
+
puts "I am dead."; @game.dark_flag = false;
|
397
|
+
@game.loc = @game.rooms.size-1; @game.look
|
398
|
+
when 62 then i = args.shift; @game.items[i].loc = args.shift
|
399
|
+
when 63 then @game.finished(0)
|
400
|
+
when 64 then @game.look
|
401
|
+
when 65 then @game.score
|
402
|
+
when 66 then @game.inventory
|
403
|
+
when 67 then @game.flags[0] = true
|
404
|
+
when 68 then @game.flags[0] = false
|
405
|
+
when 69 then
|
406
|
+
@game.items[ITEM_LAMP].loc = ROOM_CARRIED
|
407
|
+
@game.lampleft = @game.lamptime
|
408
|
+
@game.flags[FLAG_LAMPDEAD] = false
|
409
|
+
when 70 then # do nothing
|
410
|
+
when 71 then @game.prompt_and_save
|
411
|
+
when 72 then
|
412
|
+
item1 = @game.items[args.shift]
|
413
|
+
item2 = @game.items[args.shift]
|
414
|
+
item1.loc, item2.loc = item2.loc, item1.loc
|
415
|
+
when 73 then return true
|
416
|
+
when 74 then @game.items[args.shift].loc = ROOM_CARRIED
|
417
|
+
when 75 then i1 = args.shift; i2 = args.shift
|
418
|
+
@game.items[i1].loc = @game.items[i2].loc
|
419
|
+
when 76 then @game.look
|
420
|
+
when 77 then @game.counter -= 1
|
421
|
+
when 78 then print @game.counter, " "
|
422
|
+
when 79 then @game.counter = args.shift
|
423
|
+
when 80 then @game.loc, @game.saved_room = @game.saved_room, @game.loc
|
424
|
+
when 81 then which = args.shift
|
425
|
+
@game.counter, @game.counters[which] =
|
426
|
+
@game.counters[which], @game.counter
|
427
|
+
when 82 then @game.counter += args.shift
|
428
|
+
when 83 then @game.counter -= args.shift
|
429
|
+
#84 print_noun unused in Adventureland/Pirate
|
430
|
+
when 85 then puts @game.noun
|
431
|
+
when 86 then puts
|
432
|
+
when 87 then which = args.shift
|
433
|
+
@game.loc, @game.saved_rooms[which] =
|
434
|
+
@game.saved_rooms[which], @game.loc
|
435
|
+
#87 swap_specific_room unused in Adventureland/Pirate
|
436
|
+
when 88 then sleep 2 if !@game.options[:no_wait]
|
437
|
+
#88 wait unused in Adventureland/Pirate
|
438
|
+
#89 draw unused in Adventureland/Pirate
|
439
|
+
else raise "unimplemented instruction code #@op"
|
440
|
+
end
|
441
|
+
end
|
442
|
+
return false
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
class Action
|
448
|
+
def execute(test_chance)
|
449
|
+
all_conds_true = @conds.map { |x| t = x.evaluate
|
450
|
+
@game.dputs(:show_conditions, " #{x.render()} -> #{t}"); t }.
|
451
|
+
reduce(true) { |acc, val| acc && val }
|
452
|
+
@game.dputs :show_conditions, " #{all_conds_true}"
|
453
|
+
return :failconds if !all_conds_true
|
454
|
+
|
455
|
+
if (test_chance && @verb == 0) # It's an occurrence and may be random
|
456
|
+
dice = Integer(rand()*100)
|
457
|
+
if dice >= @noun
|
458
|
+
@game.dputs :show_random, " #{dice} >= #{@noun}% -> nop"
|
459
|
+
return :success
|
460
|
+
else
|
461
|
+
@game.dputs :show_random, " #{dice} < #{@noun}%"
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
args = @args.clone
|
466
|
+
seen_continue = @instructions.reduce(false) do |acc, x|
|
467
|
+
tmp = x.execute(args)
|
468
|
+
acc || tmp
|
469
|
+
end
|
470
|
+
seen_continue ? :continue : :success
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
data/notes/Definition
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
Game
|
2
|
+
|
3
|
+
A game consists of a header of 12 values each apparently 16 bit
|
4
|
+
|
5
|
+
0 Unknown
|
6
|
+
1 Number of items
|
7
|
+
2 Number of actions
|
8
|
+
3 Number of Nouns and Verbs (one list is padded)
|
9
|
+
4 Number of rooms
|
10
|
+
5 Maximum a player can carry
|
11
|
+
6 Starting Room
|
12
|
+
7 Total Treasures (*)
|
13
|
+
8 Word Length (only seen 3,4 or 5)
|
14
|
+
9 Time light source lasts. This counts down every time item 9 is
|
15
|
+
in game. Brian Howarths games allow -1 for never run down. When
|
16
|
+
it runs out the light item (9) is dumped in room 0 and a look
|
17
|
+
done. Messages vary between interpreters and include things
|
18
|
+
like 'Your light is flickering and dying' as well as
|
19
|
+
'Light runs out in %d turns'.
|
20
|
+
10 Number of Messages
|
21
|
+
11 Room you must put treasure in to score points. Not all games use
|
22
|
+
the treasure system for scoring
|
23
|
+
|
24
|
+
All the number of something values are the last item to be read counting
|
25
|
+
from 0. Thus 3 messages, means messages 0, 1, 2 and 3.
|
26
|
+
|
27
|
+
(*) This can be calculated in game. What happens if the number is wrong
|
28
|
+
I don't know. I've found no games that it occurs in.
|
29
|
+
|
30
|
+
A game has 16 (maybe more) binary flags, and 8 (maybe more counters). A few
|
31
|
+
later games seem to have 2 (maybe more) values to store location numbers in
|
32
|
+
temporarily - eg Yoho spell in Claymorgue. Flag 15 indicates whether
|
33
|
+
it is dark, and flag 16 seems to get set when the lamp runs out. No
|
34
|
+
other flag has an intrinsic meaning.
|
35
|
+
|
36
|
+
Following the header is a list of game actions. Each is of the form
|
37
|
+
|
38
|
+
150*verb+noun
|
39
|
+
5 repeats of condition+20*value
|
40
|
+
150*action1+action2
|
41
|
+
150*action3+action4
|
42
|
+
|
43
|
+
Conditions
|
44
|
+
|
45
|
+
0 <arg> is a parameter to one of the following actions
|
46
|
+
1 Item <arg> carried
|
47
|
+
2 Item <arg> in room with player
|
48
|
+
3 Item <arg> carried or in room with player
|
49
|
+
4 In room <arg>
|
50
|
+
5 Item <arg> not in room with player
|
51
|
+
6 Item <arg> not carried
|
52
|
+
7 Not in room <arg>
|
53
|
+
8 BitFlag <arg> is set.
|
54
|
+
9 BitFlag <arg> is cleared
|
55
|
+
10 Something carried (arg unused)
|
56
|
+
11 Nothing carried (arg unused)
|
57
|
+
12 Item <arg> not carried nor in room with player
|
58
|
+
13 Item <arg> is in game [not in room 0]
|
59
|
+
14 Item <arg> is not in game [in room 0]
|
60
|
+
15 CurrentCounter <= <arg>
|
61
|
+
16 CurrentCounter > <arg>
|
62
|
+
17 Object still in initial room (arg unused)
|
63
|
+
18 Object not in initial room (arg unused)
|
64
|
+
19 CurrentCounter = <arg>
|
65
|
+
|
66
|
+
Actions. <arg>s are taken from the values provided by
|
67
|
+
pseudo-conditionals with op-code 0 in the same action.
|
68
|
+
|
69
|
+
0 Does nothing
|
70
|
+
1-51 Print message 1-51. Some drivers add a space some add a newline.
|
71
|
+
It does not seem to be possible to print message 0
|
72
|
+
52 Get item <arg>. Checks if you can carry it first
|
73
|
+
53 Drops item <arg>
|
74
|
+
54 Moves to room <arg>
|
75
|
+
55 Item <arg> is removed from the game (put in room 0)
|
76
|
+
56 The darkness flag is set
|
77
|
+
57 The darkness flag is cleared
|
78
|
+
58 Bitflag <arg> is set
|
79
|
+
59 The same as 55 (it seems - I'm cautious about this)
|
80
|
+
60 BitFlag <arg> is cleared
|
81
|
+
61 Death. Dark flag cleared, player moved to last room
|
82
|
+
62 Item <arg1> put in room <arg2>
|
83
|
+
63 Game over.
|
84
|
+
64 Describe room
|
85
|
+
65 Score
|
86
|
+
66 Inventory
|
87
|
+
67 BitFlag 0 is set
|
88
|
+
68 BitFlag 0 is cleared
|
89
|
+
69 Refill lamp (reset its time to live) and put it in player's inventory
|
90
|
+
70 Screen is cleared. This varies by driver from no effect upwards
|
91
|
+
71 Saves the game. Choices of filename etc depend on the driver alone.
|
92
|
+
72 Swap item <arg1> and item <arg2> locations
|
93
|
+
73 Continue: when finished with the current action, proceed to
|
94
|
+
attempt all subsequent actions that have both noun and verb
|
95
|
+
equal to 0 (subject to their conditions being satisfied).
|
96
|
+
74 Take item <arg> - no check is done too see if it can be carried.
|
97
|
+
75 Put item <arg1> with item <arg2> - Not certain seems to do this
|
98
|
+
from examination of Claymorgue
|
99
|
+
76 Look (same as 64 ?? - check)
|
100
|
+
77 Decrement current counter. Will not go below 0
|
101
|
+
78 Print current counter value. Some drivers only cope with 0-99
|
102
|
+
apparently
|
103
|
+
79 Set current counter value to <arg>
|
104
|
+
80 Swap location with current location-swap flag
|
105
|
+
81 Select a counter. Current counter is swapped with backup counter
|
106
|
+
<arg>
|
107
|
+
82 Add <arg> to current counter
|
108
|
+
83 Subtract <arg> from current counter
|
109
|
+
84 Echo noun player typed without CR
|
110
|
+
85 Echo the noun the player typed
|
111
|
+
86 CR
|
112
|
+
87 Swap current location value with backup location-swap value <arg>
|
113
|
+
88 Wait 2 seconds
|
114
|
+
89 SAGA - draw picture <n> (actually <n+number of rooms>, as each
|
115
|
+
Look() draws picture <room number> automatically)
|
116
|
+
Older spectrum driver - crashes
|
117
|
+
Spectrum Seas of Blood - seems to start Fighting Fantasy combat mode
|
118
|
+
90-101 Unused
|
119
|
+
102+ Print message 52-99
|
120
|
+
|
121
|
+
|
122
|
+
This is followed by the words with verbs and nouns interleaved. A word with
|
123
|
+
a * at the beginning is a synonym for the word above.
|
124
|
+
Verb 1 is GO, verb 10 is GET, verb 18 is DROP (always).
|
125
|
+
Nouns 1-6 are directions.
|
126
|
+
|
127
|
+
This is followed by the rooms. Each room is 6 exits (north south east west
|
128
|
+
up down) followed by a text string.
|
129
|
+
|
130
|
+
Then come the messages, stored as a list of strings.
|
131
|
+
|
132
|
+
Next come the items in the format item text then location. Item text may
|
133
|
+
end with /TEXT/. This text is not printed but means that an automatic
|
134
|
+
get/drop will be done for 'GET/DROP TEXT' on this item. Item names beginning
|
135
|
+
with '*' are treasures. The '*' is printed. If you put all treasures in the
|
136
|
+
treasure room (in the header) and 'SCORE' the game finishes with a well done
|
137
|
+
message. Item location -1 is the inventory (255 on C64 and Spectrum tape
|
138
|
+
games) and 0 means not in play in every game I've looked at. The lamp (always
|
139
|
+
object 9) behaviour supports this belief.
|
140
|
+
|
141
|
+
A set of strings follow this. In order they match to each line of verb/noun
|
142
|
+
actions, and are comments. The Spectrum and C64 tape system where the
|
143
|
+
database is compiled into the program has no comments.
|
144
|
+
|
145
|
+
Finally three values follow which are version, adventure number and an
|
146
|
+
unknown magic number.
|
147
|
+
|