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.
Files changed (67) hide show
  1. data/.gitignore +4 -0
  2. data/GPL-2 +339 -0
  3. data/Makefile +5 -0
  4. data/README +75 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/bin/scottkit +96 -0
  8. data/bin/scottkit.rb +96 -0
  9. data/data/.gitignore +1 -0
  10. data/data/adams/.gitignore +2 -0
  11. data/data/adams/AdamsGames.zip +0 -0
  12. data/data/adams/Makefile +18 -0
  13. data/data/crystal/crystal.map +112 -0
  14. data/data/crystal/crystal.sck +598 -0
  15. data/data/crystal/crystal.solution +82 -0
  16. data/data/dan-and-matt.sck +180 -0
  17. data/data/dan-and-matt.solution +32 -0
  18. data/data/howarth/.gitignore +2 -0
  19. data/data/howarth/Makefile +14 -0
  20. data/data/howarth/mysterious.tar.gz +0 -0
  21. data/data/test/Makefile +18 -0
  22. data/data/test/adams/Makefile +13 -0
  23. data/data/test/adams/adv01.solution +186 -0
  24. data/data/test/adams/adv01.transcript +869 -0
  25. data/data/test/adams/adv01.transcript.md5 +1 -0
  26. data/data/test/adams/adv02.solution +225 -0
  27. data/data/test/adams/adv02.transcript +970 -0
  28. data/data/test/adams/adv02.transcript.md5 +1 -0
  29. data/data/test/adams/adv04.solution +187 -0
  30. data/data/test/adams/adv04.transcript +876 -0
  31. data/data/test/adams/adv04.transcript.md5 +1 -0
  32. data/data/test/crystal.decompile +628 -0
  33. data/data/test/crystal.sao +373 -0
  34. data/data/test/crystal.save-file +62 -0
  35. data/data/test/crystal.save-script +41 -0
  36. data/data/test/crystal.sck +598 -0
  37. data/data/test/crystal.solution +82 -0
  38. data/data/test/crystal.transcript +413 -0
  39. data/data/test/t6.pretty-print +225 -0
  40. data/data/test/t7.sao +110 -0
  41. data/data/test/t7.solution +28 -0
  42. data/data/test/t7.transcript +147 -0
  43. data/data/tutorial/t1.sck +5 -0
  44. data/data/tutorial/t2.sck +14 -0
  45. data/data/tutorial/t3.sck +38 -0
  46. data/data/tutorial/t4.sck +62 -0
  47. data/data/tutorial/t5.sck +87 -0
  48. data/data/tutorial/t6.sck +119 -0
  49. data/data/tutorial/t7.sck +135 -0
  50. data/lib/scottkit/compile.rb +661 -0
  51. data/lib/scottkit/decompile.rb +175 -0
  52. data/lib/scottkit/game.rb +409 -0
  53. data/lib/scottkit/play.rb +474 -0
  54. data/notes/Definition +147 -0
  55. data/notes/Definition.saved-game +9 -0
  56. data/notes/Definition.scottfree-1.14 +142 -0
  57. data/notes/adventureland-maze +20 -0
  58. data/notes/continue-action +51 -0
  59. data/test/test_canonicalise.rb +94 -0
  60. data/test/test_compile.rb +54 -0
  61. data/test/test_decompile.rb +13 -0
  62. data/test/test_play.rb +20 -0
  63. data/test/test_playadams.rb +36 -0
  64. data/test/test_save.rb +31 -0
  65. data/test/withio.rb +15 -0
  66. data/test/withio_test.rb +31 -0
  67. 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
+
@@ -0,0 +1,9 @@
1
+ ScottFree saved-game format, determined from source and a sample:
2
+
3
+ counter0 roomsaved0
4
+ ...
5
+ counter15 roomsaved15
6
+ bitflags(as bigendian number) dark(redundant) loc counter savedroom lightleft
7
+ item0.location
8
+ ...
9
+ itemNitems.location