inform6lib 0.1.2 → 0.1.4

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.
@@ -60,7 +60,6 @@
60
60
  # which supports the referencing of Special Global Variables like
61
61
  # $LAST_MATCH_INFO by their screaming snake case names.
62
62
  require 'English'
63
- require 'set'
64
63
 
65
64
  require_relative 'linklpa.h'
66
65
 
@@ -249,7 +248,7 @@ module Parser
249
248
  if !defined?(MAX_TIMERS)
250
249
  MAX_TIMERS = 32 # Max number timers/daemons active at once
251
250
  end # if !defined?(MAX_TIMERS)
252
- @the_timers = [MAX_TIMERS]
251
+ @the_timers = []
253
252
  @active_timers # Number of timers/daemons actives
254
253
 
255
254
  @score # The current score
@@ -540,22 +539,22 @@ module Parser
540
539
  @selfobj ||= SelfObj
541
540
  end
542
541
 
543
- def selfobj=(o)
544
- @selfobj = o
542
+ def selfobj=(obj)
543
+ @selfobj = obj
545
544
  end
546
545
 
547
546
  InitalState = Struct.new(:memo).new
548
547
 
549
548
  def self.initial_state
550
- InitalState.memo ||= Inform::Parser.instance_variables.each_with_object({}) do |variable, memo|
551
- memo[variable] = Inform::Parser.instance_variable_get(variable)
549
+ InitalState.memo ||= Inform::Parser.instance_variables.to_h do |variable|
550
+ [variable, Inform::Parser.instance_variable_get(variable)]
552
551
  end
553
552
  end
554
553
 
555
554
  def initialize_state
556
555
  DidScopeAction.clear # TODO: Remove
557
- Inform::Parser.initial_state.each_pair do |k, v|
558
- self.instance_variable_set(k, v)
556
+ Inform::Parser.initial_state.each_pair do |key, value|
557
+ self.instance_variable_set(key, value)
559
558
  end
560
559
  end
561
560
 
@@ -816,7 +815,9 @@ module Parser
816
815
  # Note that there are three different places where a return can happen.
817
816
  # ----------------------------------------------------------------------------
818
817
 
819
- ConversationStarterPattern = %r{\w+, }.freeze
818
+ QuotesSayCommandPattern = %r{^['"]}
819
+ ConversationStarterPattern = %r{\w+, }
820
+ WordOrCommaSplitPattern = %r{\s+|(?=,)|(?<=,)}
820
821
 
821
822
  def Parser__parse
822
823
  @meta = false
@@ -843,23 +844,28 @@ module Parser
843
844
 
844
845
  if @held_back_mode
845
846
  @held_back_mode = false
846
- throw :ReParse
847
+ return ReParse()
847
848
  end
848
849
 
849
850
  # Handle the apostrophe shorthand for the Say command
850
- @input = "say '#{@input[1..]}'" if @input =~ /^['"]/
851
+ @input = "say '#{@input[1..]}'" if QuotesSayCommandPattern.match?(@input)
851
852
 
852
853
  # Get the words from the player's input
853
- @words = @input.split
854
+ # @words = @input.split
855
+ @words = @input.split(WordOrCommaSplitPattern)
854
856
 
855
- # :ReParse
857
+ ReParse()
858
+ end
859
+ # def Parser__parse
856
860
 
861
+ def ReParse
857
862
  @parser_inflection = :name
858
863
 
859
864
  # Initially assume the command is aimed at the player, and the verb
860
865
  # is the first word
861
866
 
862
867
  # num_words = WordCount()
868
+ @num_words = @words.length
863
869
  @wn = 0 # Our words "buffer" is indexed from 0, unlike Inform
864
870
 
865
871
  if defined? LanguageToInformese
@@ -912,14 +918,19 @@ module Parser
912
918
  @location ||= @actors_location # TODO: Necessary?
913
919
  @usual_grammar_after = nil
914
920
 
915
- # :AlmostReParse
921
+ AlmostReParse()
922
+ end
923
+ # def ReParse
916
924
 
925
+ def AlmostReParse
917
926
  @scope_token = nil
918
927
  @action_to_be = nil
919
928
 
920
- # Begin from what we currently think is the verb word
929
+ BeginCommand()
930
+ end
921
931
 
922
- # :BeginCommand
932
+ def BeginCommand()
933
+ # Begin from what we currently think is the verb word
923
934
 
924
935
  @wn = @verb_wordnum
925
936
  # @verb_word = NextWordStopped() # TODO: FIXME?
@@ -938,7 +949,7 @@ module Parser
938
949
 
939
950
  @verb_word = AGAIN1__WD if @verb_word == AGAIN2__WD || @verb_word == AGAIN3__WD
940
951
  if @verb_word == AGAIN1__WD
941
- raise RepeatedOrder if @actor != @player
952
+ raise RepeatedCommand if @actor != @player
942
953
  raise NothingToRepeat if history.empty?
943
954
  @input = history.last
944
955
  @words = @input.split
@@ -956,8 +967,6 @@ module Parser
956
967
  # return DoOops()
957
968
  end
958
969
 
959
- initialize_state
960
-
961
970
  if @match_list then @match_list.clear else @match_list = [] end
962
971
  if @multiple_object then @multiple_object.clear else @multiple_object = [] end
963
972
  if @pattern then @pattern.clear else @pattern = [] end
@@ -1005,7 +1014,7 @@ module Parser
1005
1014
  end # DEBUG
1006
1015
  @wn = @verb_wordnum; @indef_mode = false; @token_filter = nil
1007
1016
  l = NounDomain(Compass)
1008
- # throw :ReParse if l == REPARSE_CODE
1017
+ # return ReParse() if l == REPARSE_CODE
1009
1018
 
1010
1019
  if l
1011
1020
  # If it is a direction, send back the results:
@@ -1031,41 +1040,23 @@ module Parser
1031
1040
  # tell the wizard that she is an idiot.)
1032
1041
 
1033
1042
  if @actor == @player
1034
- if ConversationStarterPattern.match?(@words.join(' '))
1035
- # TODO: Implement conversation and imperative commands
1036
- log.debug "jump Conversation"
1037
- # jump Conversation
1038
- else
1039
- @verb = UnknownVerb(@verb)
1040
- if @verb.nil?
1041
- @best_etype = VerbUnrecognized
1042
- raise @best_etype
1043
- else
1044
- log.debug "jump VerbAccepted"
1045
- # jump VerbAccepted
1046
- end
1043
+ j = 2
1044
+ while j <= @num_words
1045
+ i = NextWord(); j += 1
1046
+ return Conversation() if i == @comma_word
1047
1047
  end
1048
- else
1049
- @best_etype = VerbUnrecognized
1050
- raise @best_etype
1048
+ @verb = UnknownVerb(@verb)
1049
+ return VerbAccepted() unless @verb.nil?
1051
1050
  end
1052
-
1053
- # Conversation()
1054
-
1055
- @actor = l
1056
- @actors_location = ScopeCeiling(l)
1057
- if defined?(DEBUG)
1058
- if @parser_trace >= 1
1059
- println "[Actor is " + the(@actor) + " in " + @actors_location + "]"
1060
- end
1061
- end # DEBUG
1062
- return true
1051
+ @best_etype = VerbUnrecognized
1052
+ return GiveError()
1053
+ # raise @best_etype
1063
1054
  end
1064
- # of first-word-not-a-verb
1055
+ # end of first-word-not-a-verb
1065
1056
 
1066
1057
  VerbAccepted()
1067
1058
  end
1068
- # def Parser__parse
1059
+ # def BeginCommand
1069
1060
 
1070
1061
  ############################################################################
1071
1062
  #
@@ -1143,46 +1134,41 @@ module Parser
1143
1134
 
1144
1135
  return true
1145
1136
  end
1146
- # def Parser__parse
1137
+ # def VerbAccepted
1147
1138
 
1148
1139
  # NextWord nudges the word number wn on by one each time, so we've now
1149
1140
  # advanced past a comma. (A comma is a word all on its own in the table.)
1150
1141
 
1151
1142
  def Conversation
1152
- j = @wn - 1
1153
- if j == 1
1154
- raise UnexpectedComma
1155
- end
1143
+ j = @wn
1144
+ raise UnexpectedComma if j == 1
1156
1145
 
1157
1146
  # Use NounDomain (in the context of "animate creature") to see if the
1158
1147
  # words make sense as the name of someone held or nearby
1159
1148
 
1160
- @wn = 1; @lookahead = HELD_TOKEN
1149
+ @wn = 0; @lookahead = :held
1161
1150
  @scope_reason = TALKING_REASON
1162
1151
  l = NounDomain(@player,@actors_location,6)
1163
1152
  @scope_reason = PARSING_REASON
1164
1153
  throw :ReParse if l == REPARSE_CODE
1165
- raise MissingAddressee if l == 0
1154
+ raise MissingAddressee if l == false
1166
1155
 
1167
- Conversation2()
1156
+ Conversation2(l, j)
1168
1157
  end
1169
1158
 
1170
- def Conversation2
1159
+ def Conversation2(l, j)
1171
1160
 
1172
1161
  # The object addressed must at least be "talkable" if not actually "animate"
1173
1162
  # (the distinction allows, for instance, a microphone to be spoken to,
1174
1163
  # without the parser thinking that the microphone is human).
1175
1164
 
1176
- if l.hasnt? :animate, :talkable
1177
- raise InanimateAddressee
1178
- end
1165
+ raise InanimateAddressee if l.hasnt? :animate, :talkable
1179
1166
 
1180
1167
  # Check that there aren't any mystery words between the end of the person's
1181
1168
  # name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
1182
1169
 
1183
- if @wn != j
1184
- raise SpeechImpediment
1185
- end
1170
+ @wn += 1
1171
+ raise SpeechImpediment if @wn != j
1186
1172
 
1187
1173
  # The player has now successfully named someone. Adjust "him", "her", "it":
1188
1174
 
@@ -1191,15 +1177,14 @@ module Parser
1191
1177
  # Set the global variable "actor", adjust the number of the first word,
1192
1178
  # and begin parsing again from there.
1193
1179
 
1194
- @verb_wordnum = j + 1
1180
+ @verb_wordnum = j
1195
1181
 
1196
1182
  # Stop things like "me, again":
1197
1183
 
1198
1184
  if l == @player
1199
1185
  @wn = @verb_wordnum
1200
1186
  if [AGAIN1__WD, AGAIN2__WD, AGAIN3__WD].include?(NextWordStopped())
1201
- L__M(:Miscellany, 20)
1202
- jump ReType
1187
+ raise RepeatedCommand
1203
1188
  end
1204
1189
  end
1205
1190
 
@@ -1316,7 +1301,8 @@ module Parser
1316
1301
  prepositions = @line_token[@pcount].split(/\//).map { |s| s[1..-2] }
1317
1302
  @pcount += 1 while @line_ttype[@pcount] == :preposition
1318
1303
 
1319
- if ElementaryTokens.include?(@line_ttype[@pcount]) && @line_tdata[@pcount] == :noun
1304
+ if ElementaryTokens.include?(@line_ttype[@pcount]) &&
1305
+ @line_tdata[@pcount] == :noun
1320
1306
 
1321
1307
  # Advance past the last preposition
1322
1308
 
@@ -1402,6 +1388,7 @@ module Parser
1402
1388
  @scope_reason = PARSING_REASON
1403
1389
  AnalyseToken(token)
1404
1390
 
1391
+ # TODO: FIXME
1405
1392
  # if @action_to_be == :AskTo && ElementaryTokens.include?(@found_ttype) &&
1406
1393
  # @found_tdata == :topic
1407
1394
  # l = @results[2]
@@ -1414,7 +1401,8 @@ module Parser
1414
1401
 
1415
1402
  if defined?(DEBUG) && @parser_trace >= 10
1416
1403
  new_line
1417
- println "Parsing the current word '#{current_word}' as a #{@found_ttype} in this grammar:"
1404
+ println "Parsing the current word '#{current_word}' " +
1405
+ "as a #{@found_ttype} in this grammar:"
1418
1406
  println grammar.to_s
1419
1407
  println ">>Before invoking ParseToken(#{@found_ttype})..."
1420
1408
  println ">>...The found token data is #{@found_tdata}"
@@ -1603,7 +1591,8 @@ module Parser
1603
1591
  # We continue the outer "for" loop, trying the next line in the grammar.
1604
1592
 
1605
1593
  @best_etype = @etype if @etype > @best_etype
1606
- @nextbest_etype = @etype if @etype != AskScope && !@etype.nil? && @etype > @nextbest_etype
1594
+ @nextbest_etype = @etype if @etype != AskScope &&
1595
+ !@etype.nil? && @etype > @nextbest_etype
1607
1596
 
1608
1597
  # ...unless the line was something like "take all" which failed because
1609
1598
  # nothing matched the "all", in which case we stop and give an error now.
@@ -1615,14 +1604,17 @@ module Parser
1615
1604
 
1616
1605
  # The grammar is exhausted: every line has failed to match.
1617
1606
 
1607
+ GiveError()
1608
+ end
1609
+ # def BestGrammar
1610
+
1618
1611
  ############################################################################
1619
1612
  #
1620
1613
  # H: Cheaply parse otherwise unrecognised conversation and return
1621
1614
  #
1622
1615
  ############################################################################
1623
1616
 
1624
- # label :GiveError do
1625
-
1617
+ def GiveError
1626
1618
  if defined?(DEBUG) && @parser_trace >= 10
1627
1619
  println ">>@etype is #{@etype}"
1628
1620
  println ">>@best_etype is #{@best_etype}"
@@ -1633,26 +1625,25 @@ module Parser
1633
1625
  # If the command was addressed to somebody else (eg, "dwarf, sfgh") then
1634
1626
  # it is taken as conversation which the parser has no business in disallowing.
1635
1627
 
1636
- # TODO: Implement conversations
1637
- # if @actor != @player
1638
- # if @usual_grammar_after
1639
- # @verb_wordnum = @usual_grammar_after
1640
- # throw :AlmostReParse
1641
- # end
1642
- # @wn = @verb_wordnum
1643
- # @special_word = NextWord()
1644
- # if @special_word == @comma_word
1645
- # @special_word = NextWord()
1646
- # @verb_wordnum++
1647
- # end
1648
- # @special_number = TryNumber(@verb_wordnum)
1649
- # @results[0] = :NotUnderstood
1650
- # @results[1] = 2
1651
- # @results[2] = 1; @special_number1 = @special_word
1652
- # @results[3] = @actor
1653
- # @consult_from = @verb_wordnum; @consult_words = @num_words-@consult_from+1
1654
- # return true
1655
- # end
1628
+ if @actor != @player
1629
+ unless @usual_grammar_after.nil?
1630
+ @verb_wordnum = @usual_grammar_after
1631
+ return AlmostReParse()
1632
+ end
1633
+ @wn = @verb_wordnum
1634
+ @special_word = NextWord()
1635
+ if @special_word == @comma_word
1636
+ @special_word = NextWord()
1637
+ @verb_wordnum += 1
1638
+ end
1639
+ @special_number = TryNumber(@verb_wordnum)
1640
+ @results[0] = :NotUnderstood
1641
+ @results[1] = 2
1642
+ @results[2] = 1; @special_number1 = @special_word
1643
+ @results[3] = @actor
1644
+ @consult_from = @verb_wordnum; @consult_words = @num_words-@consult_from+1
1645
+ return true
1646
+ end
1656
1647
 
1657
1648
  ############################################################################
1658
1649
  #
@@ -1679,7 +1670,7 @@ module Parser
1679
1670
  # ...being careful not to go all the way back, to avoid infinite repetition
1680
1671
  # of a deferred command causing an error.
1681
1672
  end
1682
- # def BestGrammar
1673
+ # def GiveError
1683
1674
 
1684
1675
  ############################################################################
1685
1676
  #
@@ -1896,7 +1887,7 @@ module Parser
1896
1887
  # (F) Return the conclusion of parsing an object list
1897
1888
  # ----------------------------------------------------------------------------
1898
1889
 
1899
- CommaWordPattern = %r{,$}.freeze
1890
+ CommaWordPattern = %r{,$}
1900
1891
 
1901
1892
  # rubocop: disable Layout/EmptyLinesAroundBlockBody
1902
1893
  def ParseToken(given_ttype, given_tdata, token_n, token)
@@ -2041,7 +2032,10 @@ module Parser
2041
2032
 
2042
2033
  end
2043
2034
 
2044
- token = given_ttype
2035
+ # TODO: Remove (2026-08-21)
2036
+ # token = given_ttype
2037
+ # TODO: Test
2038
+ token = given_tdata
2045
2039
  prev_indef_wanted = 0
2046
2040
 
2047
2041
  ############################################################################
@@ -2116,7 +2110,9 @@ module Parser
2116
2110
  end
2117
2111
  end
2118
2112
 
2119
- if [ME1__WD, ME2__WD, ME3__WD].include?(o) then @pronoun_word = o; @pronoun_obj = selfobj; end
2113
+ if [ME1__WD, ME2__WD, ME3__WD].include?(o)
2114
+ @pronoun_word = o; @pronoun_obj = selfobj
2115
+ end
2120
2116
 
2121
2117
  @allow_plurals = true; desc_wn = @wn;
2122
2118
 
@@ -2197,7 +2193,8 @@ module Parser
2197
2193
  @wn = desc_wn
2198
2194
  throw :TryAgain2
2199
2195
  end
2200
- @etype = CannotUnderstandSentence if @multiflag && [UnexpectedMultiple, TooFew].include?(@etype)
2196
+ @etype = CannotUnderstandSentence if @multiflag &&
2197
+ [UnexpectedMultiple, TooFew].include?(@etype)
2201
2198
  @etype = CantSee()
2202
2199
  return FailToken(desc_wn, prev_indef_wanted)
2203
2200
  end # Choose best error
@@ -2749,7 +2746,8 @@ module Parser
2749
2746
  next unless @pattern[j]
2750
2747
 
2751
2748
  if defined?(DEBUG)
2752
- println "[Gluing in inference with pattern code " + @pattern[j] + "]" if @parser_trace >= 5
2749
+ println "[Gluing in inference with pattern code " +
2750
+ @pattern[j] + "]" if @parser_trace >= 5
2753
2751
  end # DEBUG
2754
2752
 
2755
2753
  # Conveniently, parse2[1] is the first word in both ZCODE and GLULX.
@@ -2975,7 +2973,8 @@ module Parser
2975
2973
  end
2976
2974
  @multi_context = context
2977
2975
  if defined?(DEBUG)
2978
- println " Made multiple object of size #{@multiple_object.length}]" if @parser_trace >= 4
2976
+ println " Made multiple object of size " +
2977
+ "#{@multiple_object.length}]" if @parser_trace >= 4
2979
2978
  end # DEBUG
2980
2979
  return 1
2981
2980
  end
@@ -3007,7 +3006,8 @@ module Parser
3007
3006
  println " Grouped into #{n} possibilities by name:"
3008
3007
  for i in (0...@number_matched)
3009
3008
  if @match_classes[i] > 0
3010
- print " " + The(@match_list[i]) + " (#{@match_list[i].object_id}) --- group #{@match_classes[i]}\n"
3009
+ print " " + The(@match_list[i]) +
3010
+ " (#{@match_list[i].object_id}) --- group #{@match_classes[i]}\n"
3011
3011
  end
3012
3012
  end
3013
3013
  end
@@ -3097,7 +3097,8 @@ module Parser
3097
3097
  # entries which fail the basic requirements of the descriptors.
3098
3098
  # ----------------------------------------------------------------------------
3099
3099
 
3100
- def ScoreMatchL(context, match_list = [], match_scores = [], number_matched = 0, threshold = 0)
3100
+ def ScoreMatchL(context, match_list = [], match_scores = [],
3101
+ number_matched = 0, threshold = 0)
3101
3102
  threshold += 1 if (@indef_type & OTHER_BIT) != 0
3102
3103
  threshold += 1 if (@indef_type & MY_BIT) != 0
3103
3104
  threshold += 1 if (@indef_type & THAT_BIT) != 0
@@ -3129,7 +3130,8 @@ module Parser
3129
3130
  for i in (0...number_matched)
3130
3131
  obj = match_list[i]; its_owner = parent(obj); its_score = 0; met = 0
3131
3132
 
3132
- # met+=1 if (@indef_type & OTHER_BIT) != 0 && obj != @itobj && obj != @himobj && obj != @herobj
3133
+ # met+=1 if (@indef_type & OTHER_BIT) != 0 &&
3134
+ # obj != @itobj && obj != @himobj && obj != @herobj
3133
3135
  met += 1 if (@indef_type & MY_BIT) != 0 && (its_owner == @actor)
3134
3136
  met += 1 if (@indef_type & THAT_BIT) != 0 && (its_owner == @actors_location)
3135
3137
  met += 1 if (@indef_type & LIT_BIT) != 0 && obj.has?(:light)
@@ -3209,7 +3211,9 @@ module Parser
3209
3211
  if defined?(DEBUG)
3210
3212
  if @parser_trace >= 4
3211
3213
  if best < 0 then println " Best guess ran out of choices"
3212
- else println " Best guess " + the(@match_list[earliest]) + " (#{@match_list[earliest]})"
3214
+ else
3215
+ println " Best guess " + the(@match_list[earliest]) +
3216
+ " (#{@match_list[earliest]})"
3213
3217
  end
3214
3218
  end
3215
3219
  end # DEBUG
@@ -3301,7 +3305,8 @@ module Parser
3301
3305
 
3302
3306
  # def PrintInferredCommand(from = 0)
3303
3307
  # singleton_noun = false
3304
- # if from != 0 && from == (@pcount-1) && @pattern[from].object? # > 1 && @pattern[from] < REPARSE_CODE
3308
+ # if from != 0 && from == (@pcount-1) &&
3309
+ # @pattern[from].object? # > 1 && @pattern[from] < REPARSE_CODE
3305
3310
  # singleton_noun = true
3306
3311
  # end
3307
3312
 
@@ -3310,7 +3315,8 @@ module Parser
3310
3315
 
3311
3316
  # def PrintInferredCommand(from = nil)
3312
3317
  # singleton_noun = false
3313
- # if from != 0 && from == @pcount-1 && @pattern[from].object? # && @pattern[from] < REPARSE_CODE
3318
+ # if from != 0 && from == @pcount-1 &&
3319
+ # @pattern[from].object? # && @pattern[from] < REPARSE_CODE
3314
3320
  # singleton_noun = true
3315
3321
  # end
3316
3322
  # if singleton_noun == true
@@ -3593,10 +3599,7 @@ module Parser
3593
3599
  if @parser_trace >= 6
3594
3600
  println "[DSA on " + the(thing) + " with reason = " + scope_reason.to_s +
3595
3601
  " p1 = #{@parser_one} p2 = #{@parser_two}]"
3596
- # TODO: Remove
3597
- if name(thing) == 'Nels'
3598
- caller.each { |t| println t }
3599
- elsif DidScopeAction[thing][scope_reason] > 1
3602
+ if DidScopeAction[thing][scope_reason] > 1
3600
3603
  println "Did DoScopeAction(#{thing}) with reason = #{scope_reason} more than once!"
3601
3604
  caller.each { |t| println t }
3602
3605
  end
@@ -3604,13 +3607,13 @@ module Parser
3604
3607
  end # DEBUG
3605
3608
  case scope_reason
3606
3609
  when REACT_BEFORE_REASON
3607
- return unless thing.react_before?(action)
3610
+ return unless thing.react_before?(@action)
3608
3611
  if defined?(DEBUG)
3609
3612
  println "[Considering react_before for " + the(thing) + "]" if @parser_trace >= 2
3610
3613
  end
3611
3614
  @parser_one ||= RunRoutines(thing, :react_before)
3612
3615
  when REACT_AFTER_REASON
3613
- return unless thing.react_after?(action)
3616
+ return unless thing.react_after?(@action)
3614
3617
  if defined?(DEBUG)
3615
3618
  println "[Considering react_after for " + the(thing) + "]" if @parser_trace >= 2
3616
3619
  end # DEBUG
@@ -3790,7 +3793,8 @@ module Parser
3790
3793
  println ">>...Matches are [#{matches_}] ([#{object_ids_}])"
3791
3794
  end
3792
3795
  println ">>...No matches yet" if @match_list.empty?
3793
- println ">>...Matched by #{@match_length} word" + (!@match_length.nil? && @match_length == 1 ? 's' : '')
3796
+ println ">>...Matched by #{@match_length} word" +
3797
+ (!@match_length.nil? && @match_length == 1 ? 's' : '')
3794
3798
  println ">>...Match is #{obj} (#{obj.object_id})"
3795
3799
  already_in_list = @match_list.include?(obj) ? 'Yes' : 'No'
3796
3800
  println ">>...Is object id #{obj.object_id} already in the match list? #{already_in_list}"
@@ -3866,7 +3870,8 @@ module Parser
3866
3870
 
3867
3871
  def TryGivenObject(obj)
3868
3872
  if defined?(DEBUG)
3869
- println " Trying " + the(obj) + " (#{obj.object_id}) at word #{@wn + 1}" if @parser_trace >= 5
3873
+ println " Trying " + the(obj) +
3874
+ " (#{obj.object_id}) at word #{@wn + 1}" if @parser_trace >= 5
3870
3875
  end # DEBUG
3871
3876
 
3872
3877
  @dict_flags_of_noun = 0
@@ -4060,7 +4065,7 @@ module Parser
4060
4065
  return NextWord()
4061
4066
  end
4062
4067
 
4063
- def WordAddress(wordnum); @words[wordnum]; end
4068
+ def WordAddress(wordnum); @buffer[wordnum]; end
4064
4069
 
4065
4070
  def WordLength(wordnum); @words[wordnum] ? @words[wordnum].length : 0; end
4066
4071
 
@@ -4196,20 +4201,20 @@ module Parser
4196
4201
  i = @lightflag
4197
4202
  @lightflag = OffersLight(parent(@player))
4198
4203
 
4199
- if (i.nil? || i == false) && @lightflag
4204
+ if i == false && @lightflag == true
4200
4205
  @location = @real_location
4201
4206
  log.debug "Looking after light adjustment for #{@player}" # TODO: Remove
4202
- return _invoke :Look unless flag
4207
+ return _invoke :Look if flag == false
4203
4208
  end
4204
4209
 
4205
- if !i.nil? && !@lightflag
4210
+ if i == true && @lightflag == false
4206
4211
  @real_location = @location; @location = thedark
4207
- unless flag
4212
+ if flag == false
4208
4213
  NoteArrival()
4209
4214
  return L__M(:Miscellany, 9)
4210
4215
  end
4211
4216
  end
4212
- @location = thedark if !i.nil? && !@lightflag
4217
+ @location = thedark if i == false && @lightflag == false
4213
4218
  end
4214
4219
 
4215
4220
  def OffersLight(i)
@@ -4219,8 +4224,10 @@ module Parser
4219
4224
  if i.has?(:container)
4220
4225
  return OffersLight(parent(i)) if i.has?(:open) || i.has?(:transparent)
4221
4226
  else
4222
- return OffersLight(parent(i)) if i.has?(:enterable) || i.has?(:transparent) || i.has?(:supporter)
4227
+ return OffersLight(parent(i)) if i.has?(:enterable) ||
4228
+ i.has?(:transparent) || i.has?(:supporter)
4223
4229
  end
4230
+
4224
4231
  false
4225
4232
  end
4226
4233
 
@@ -4271,7 +4278,7 @@ module Parser
4271
4278
  i = @player; i = parent(i) until parent(i).nil?
4272
4279
  @location = i; @real_location = @location
4273
4280
  return RunTimeError(10) unless parent(@player)
4274
- # MoveFloatingObjects()
4281
+ MoveFloatingObjects()
4275
4282
  @lightflag = OffersLight(parent(@player))
4276
4283
  @location = thedark unless @lightflag
4277
4284
  @print_player_flag = flag
@@ -4358,7 +4365,7 @@ module Parser
4358
4365
  alias ShowverbSub ShowVerbSub
4359
4366
 
4360
4367
  KnownNumberProperties = %i[number capacity time_left].freeze
4361
- MethodWriterPattern = %r{=$}.freeze
4368
+ MethodWriterPattern = %r{=$}
4362
4369
 
4363
4370
  def ShowobjSub
4364
4371
  f = 0
@@ -4761,11 +4768,13 @@ module Parser
4761
4768
  when String then print "<string ~#{o}~>"; return true
4762
4769
  when NilClass then print "<illegal object number " + o + ">"; return true
4763
4770
  end
4764
- if o == player then print YOURSELF__TX; return true; end
4771
+ if o == @player then print YOURSELF__TX; return true; end
4765
4772
  if defined? LanguagePrintShortName
4766
4773
  return true if LanguagePrintShortName(o)
4767
4774
  end # LanguagePrintShortName
4768
- return true if @indef_mode && o.respond_to?(:short_name_indef) && PrintOrRun(o, :short_name_indef, 1) != false
4775
+ return true if @indef_mode &&
4776
+ o.respond_to?(:short_name_indef) &&
4777
+ PrintOrRun(o, :short_name_indef, 1) != false
4769
4778
  return true if !o.&(:short_name).nil? && PrintOrRun(o, :short_name, 1) != false
4770
4779
  print o
4771
4780
  end
@@ -4951,7 +4960,7 @@ module Parser
4951
4960
 
4952
4961
  class NothingToRepeat < StandardError; end
4953
4962
 
4954
- class RepeatedOrder < StandardError; end
4963
+ class RepeatedCommand < StandardError; end
4955
4964
 
4956
4965
  class SpeechImpediment < StandardError; end
4957
4966
 
@@ -5010,48 +5019,45 @@ class InformLibrary < Inform::Ephemeral::Object
5010
5019
  def play
5011
5020
  @real_location = thedark
5012
5021
  @player = selfobj; @actor = @player
5013
- selfobj.capacity = MAX_CARRIED # ### change?
5022
+ log.debug "Setting capacity of #{selfobj.identity} to #{MAX_CARRIED}"
5023
+ selfobj.&(:capacity, MAX_CARRIED) # ### change?
5024
+ log.debug " selfobj.&(:capacity): #{selfobj.&(:capacity)}"
5014
5025
 
5015
5026
  new_line
5016
5027
  LibraryExtensions.RunAll(:ext_initialise)
5017
5028
  j = Initialise()
5029
+ # Ruby doesn't automatically print implicitly returned strings :(
5018
5030
  println j if j.is_a?(String)
5031
+ @last_score = @score
5019
5032
  move @player, @location
5020
- light_adjusted @player
5021
5033
 
5022
5034
  @location = @location.parent until @location.parent.nil?
5023
5035
  @real_location = @location
5024
5036
  @actor = @player # resync, because player may have been changed in initialise()
5025
5037
  Banner() if j != 2
5038
+ MoveFloatingObjects()
5026
5039
  @lightflag = OffersLight(@player.parent)
5027
5040
  if @lightflag == false
5028
5041
  @real_location = @location
5029
5042
  @location = @thedark
5030
5043
  end
5031
- OBIT_HEAD() if deadflag
5032
- @player.mute if deadflag
5044
+ OBIT_HEAD() if deadflag != 0
5033
5045
  invoke :Look
5034
- deadflag ? :death : :playing
5046
+ deadflag != 0 ? :death : :playing
5035
5047
  end
5036
5048
 
5037
5049
  def playing
5038
- @player.unmute if @player.respond_to?(:unmute) && !deadflag
5050
+ @player.unmute if @player.respond_to?(:unmute) && deadflag != 0
5039
5051
  enqueue(command: @buffer.dup)
5040
5052
 
5041
- unless deadflag
5042
- return session.state if respond_to?(:session) && !session.nil?
5043
- return __method__
5053
+ if deadflag != 0
5054
+ log.debug "#{@player} is entering the death state" # TODO: Remove
5055
+ :death
5044
5056
  end
5045
5057
 
5046
- AfterLife() if deadflag != 2
5047
- return very__late__error if deadflag == 0
5058
+ return session.state if respond_to?(:session) && !session.nil?
5048
5059
 
5049
- OBIT_HEAD()
5050
- ScoreSub()
5051
- DisplayStatus()
5052
- AfterGameOver()
5053
- log.debug "#{@player} is entering the death state" # TODO: Remove
5054
- :death
5060
+ __method__
5055
5061
  end
5056
5062
 
5057
5063
  def stop
@@ -5092,18 +5098,48 @@ class InformLibrary < Inform::Ephemeral::Object
5092
5098
 
5093
5099
  @results = [nil, 0, nil, nil]; @meta = false
5094
5100
 
5095
- 2.times do # NotHeld
5096
- catch :ReParse do
5101
+ # i = 0
5102
+ # catch(:continue) do
5103
+ # rubocop: disable Lint/UnreachableLoop
5104
+ while @deadflag == 0 # everything happens in this loop
5105
+ return very__late__error()
5106
+ end
5107
+ # rubocop: enable Lint/UnreachableLoop
5108
+ # end
5109
+ AfterLife() if deadflag != 2
5110
+ return very__late__error if deadflag == 0
5111
+
5112
+ OBIT_HEAD()
5113
+ ScoreSub()
5114
+ DisplayStatus()
5115
+ AfterGameOver()
5116
+ end
5117
+
5118
+ def very__late__error
5119
+ if @score != @last_score
5120
+ NotifyTheScore() if @notify_mode == true
5121
+ @last_score = @score
5122
+ end
5123
+ late__error()
5124
+ rescue NoVerbRoutine, NotImplemented
5125
+ "Sorry, that action is not yet implemented."
5126
+ rescue NoMethodError => e
5127
+ parser_error e, "Sorry, there was a program error involving a missing method."
5128
+ rescue TypeError => e
5129
+ parser_error e, "Sorry, there was a program error involving the wrong type of object."
5130
+ rescue StandardError => e
5131
+ parser_error e, "Sorry, there was an unhandled program error."
5132
+ end
5133
+
5134
+ def late__error
5135
+
5097
5136
  # The Parser writes its results into @results and @meta,
5098
5137
  # a flag indicating a "meta-verb". This can only be set for
5099
5138
  # commands by the player, not for orders to others.
5100
5139
 
5101
- parse_input # inputobjs is now just results
5102
-
5103
- return if deadflag && !@meta
5104
- end
5105
- # ReParse
5140
+ parse_input
5106
5141
 
5142
+ # (Port note: inputobjs is now just results.)
5107
5143
  @action = @results[0]
5108
5144
 
5109
5145
  # --------------------------------------------------------------
@@ -5135,119 +5171,18 @@ class InformLibrary < Inform::Ephemeral::Object
5135
5171
  @multiflag = false; @onotheld_mode = @notheld_mode; @notheld_mode = false
5136
5172
  # For implicit taking and multiple object detection
5137
5173
 
5138
- # label :begin__action do
5139
- @inp1 = nil; @inp2 = nil; i = @results[1]
5140
- @inp1 = @results[2] if i >= 1
5141
- @inp2 = @results[3] if i >= 2
5142
-
5143
- # inp1 and inp2 hold: object numbers, or 0 for "multiple object",
5144
- # or 1 for "a number or dictionary address"
5145
-
5146
- if @inp1 == 1 then @noun = @special_number1 else @noun = @inp1 end
5147
- if @inp2 == 1
5148
- if @inp1 == 1 then @second = @special_number2
5149
- else @second = @special_number1
5150
- end
5151
- else @second = @inp2
5152
- end
5153
-
5154
- # --------------------------------------------------------------
5155
-
5156
- # TODO: FIXME
5157
- # act_requester = nil
5158
- #
5159
- # if @actor != @player
5160
- #
5161
- # # The player's "orders" property can refuse to allow conversation
5162
- # # here, by returning true. If not, the order is sent to the
5163
- # # other person's "orders" property. If that also returns false,
5164
- # # then: if it was a misunderstood command anyway, it is converted
5165
- # # to an Answer action (thus "floyd, grrr" ends up as
5166
- # # "say grrr to floyd"). If it was a good command, it is finally
5167
- # # offered to the Order: part of the other person's "life"
5168
- # # property, the old-fashioned way of dealing with conversation.
5169
- #
5170
- # j = RunRoutines(@player, :orders)
5171
- # unless j
5172
- # j = RunRoutines(@actor, :orders)
5173
- # unless j
5174
- # if action == :NotUnderstood
5175
- # @results[3] = @actor; @actor = @player; @action = :Answer
5176
- # #jump begin__action
5177
- # end
5178
- # if RunLife(@actor, :Order) == 0; L__M(:Order, 1, @actor); end
5179
- # end
5180
- # end
5181
- # return # jump turn__end
5182
- # end
5183
-
5184
- # --------------------------------------------------------------
5185
- # Generate the action...
5186
-
5187
- if (i == 0) ||
5188
- (i == 1 && @inp1 != 0) ||
5189
- (i == 2 && @inp1 != 0 && @inp2 != 0) ||
5190
- (i == @parameters && @inp1 != 0 && @inp2 != 0)
5191
- result = self.begin_action(@action, @noun, @second, 0)
5192
- # jump turn__end
5193
- else
5194
-
5195
- # ...unless a multiple object must be substituted. First:
5196
- # (a) check the multiple list isn't empty;
5197
- # (b) warn the player if it has been cut short because too long;
5198
- # (c) generate a sequence of actions from the list
5199
- # (stopping in the event of death or movement away).
5200
-
5201
- @multiflag = true
5202
- if @multiple_object.empty?
5203
- return L__M(:Miscellany, 2)
5204
- # jump late__error
5205
- end
5206
- if @toomany_flag
5207
- @toomany_flag = false
5208
- print L__M(:Miscellany, 1)
5209
- end
5210
- i = @location
5211
- for l in @multiple_object
5212
- return if deadflag
5213
- return L__M(:Miscellany, 51) if @location != i
5214
- PronounNotice(l)
5215
- @second = @results[3]
5216
- print "#{l}: "
5217
- if @inp1 == 0
5218
- @inp1 = l; zmsnp self.begin_action(@action, l, @second, 0); @inp1 = 0
5219
- else
5220
- @inp2 = l; zmsnp self.begin_action(@action, @noun, l, 0); @inp2 = 0
5221
- end
5222
- end
5223
- end
5224
-
5225
- # --------------------------------------------------------------
5226
-
5227
- # label :turn__end do
5174
+ begin__action()
5228
5175
 
5229
- # No time passes if either (i) the verb was meta, or
5230
- # (ii) we've only had the implicit take before the "real"
5231
- # action to follow.
5232
- if @notheld_mode
5233
- NoteObjectAcquisitions()
5234
- println(result, should_prompt: false)
5235
- return true if result == false
5236
- next
5237
- end
5238
- return result if @meta
5239
- if defined? SINGLE_PLAYER
5240
- self.end_turn_sequence() unless deadflag
5241
- end
5242
- println(result) if result.is_a?(String)
5243
- # return result if defined? result
5244
- return true
5245
- end
5246
- # 2.times do # NotHeld
5247
- rescue RepeatedOrder
5248
- return L__M(:Miscellany, REPEATED_ORDER)
5176
+ return session.state if respond_to?(:session) && !session.nil?
5177
+ true
5178
+ rescue RepeatedCommand
5179
+ return L__M(:Miscellany, REPEATED_COMMAND)
5249
5180
  rescue NothingToRepeat
5250
5181
  return L__M(:Miscellany, NOTHING_TO_REPEAT)
5182
+ rescue UnexpectedComma
5183
+ return L__M(:Miscellany, UNEXPECTED_COMMA)
5184
+ rescue MissingAddressee
5185
+ return L__M(:Miscellany, MISSING_ADDRESSEE)
5251
5186
  rescue InanimateAddressee
5252
5187
  return L__M(:Miscellany, CANT_TALK, noun)
5253
5188
  rescue SpeechImpediment
@@ -5325,46 +5260,125 @@ class InformLibrary < Inform::Ephemeral::Object
5325
5260
  return L__M(:Miscellany, NO_OBJECT)
5326
5261
  rescue CycleError
5327
5262
  return L__M(:Miscellany, TREE_ERROR)
5328
- rescue NoVerbRoutine, NotImplemented
5329
- return "Sorry, that action is not yet implemented."
5330
- rescue NoMethodError => e
5331
- log.error "Error parsing user command: #{e.class}: #{e}"
5332
- e.backtrace.each { |t| log.error t }
5333
- log.error "Parser was #{self.identity}"
5334
- log.error "Input was: #{@input}"
5335
- log.error "Actor was: #{@actor}"
5336
- log.error "Action was: #{@action}"
5337
- return "Sorry, something went wrong."
5338
- rescue TypeError => e
5339
- log.error "Error parsing user command: #{e.class}: #{e}"
5340
- e.backtrace.each { |t| log.error t }
5341
- log.error "Parser was #{self.identity}"
5342
- log.error "Input was: #{@input}"
5343
- log.error "Actor was: #{@actor}"
5344
- log.error "Action was: #{@action}"
5345
- return "Sorry, something went wrong."
5346
- rescue StandardError => e
5347
- log.error "Error parsing user command: #{e.class}: #{e}"
5348
- e.backtrace.each { |t| log.error t }
5349
- log.error "Parser was #{self.identity}"
5350
- log.error "Input was: #{@input}"
5351
- log.error "Actor was: #{@actor}"
5352
- log.error "Action was: #{@action}"
5353
- return "Sorry, something went seriously wrong."
5354
5263
  end
5355
- # def parse
5264
+ # def late__error
5265
+
5266
+ def begin__action()
5267
+ @inp1 = nil; @inp2 = nil; i = @results[1]
5268
+ @inp1 = @results[2] if i >= 1
5269
+ @inp2 = @results[3] if i >= 2
5270
+
5271
+ # inp1 and inp2 hold: object numbers, or 0 for "multiple object",
5272
+ # or 1 for "a number or dictionary address"
5273
+
5274
+ if @inp1 == 1 then @noun = @special_number1 else @noun = @inp1 end
5275
+ if @inp2 == 1
5276
+ if @inp1 == 1 then @second = @special_number2
5277
+ else @second = @special_number1
5278
+ end
5279
+ else @second = @inp2
5280
+ end
5281
+
5282
+ # --------------------------------------------------------------
5283
+
5284
+ if @actor != @player
5285
+
5286
+ # The player's "orders" property can refuse to allow conversation
5287
+ # here, by returning true. If not, the order is sent to the
5288
+ # other person's "orders" property. If that also returns false,
5289
+ # then: if it was a misunderstood command anyway, it is converted
5290
+ # to an Answer action (thus "floyd, grrr" ends up as
5291
+ # "say grrr to floyd"). If it was a good command, it is finally
5292
+ # offered to the Order: part of the other person's "life"
5293
+ # property, the old-fashioned way of dealing with conversation.
5294
+
5295
+ j = RunRoutines(@player, :orders)
5296
+ if j == false || j.nil?
5297
+ j = RunRoutines(@actor, :orders)
5298
+ if j == false || j.nil?
5299
+ if @action == :NotUnderstood
5300
+ @results[3] = @actor; @actor = @player; @action = :Answer
5301
+ return begin__action()
5302
+ end
5303
+ if RunLife(@actor, :Order) == 0 then L__M(:Order, 1, @actor); end
5304
+ end
5305
+ end
5306
+ return turn__end()
5307
+ end
5308
+
5309
+ # --------------------------------------------------------------
5310
+ # Generate the action...
5311
+
5312
+ if (i == 0) ||
5313
+ (i == 1 && @inp1 != 0) ||
5314
+ (i == 2 && @inp1 != 0 && @inp2 != 0) ||
5315
+ (i == @parameters && @inp1 != 0 && @inp2 != 0)
5316
+ result = self.begin_action(@action, @noun, @second, 0)
5317
+ print_routine_result(result)
5318
+ return turn__end(result)
5319
+ end
5320
+
5321
+ # ...unless a multiple object must be substituted. First:
5322
+ # (a) check the multiple list isn't empty;
5323
+ # (b) warn the player if it has been cut short because too long;
5324
+ # (c) generate a sequence of actions from the list
5325
+ # (stopping in the event of death or movement away).
5326
+
5327
+ @multiflag = true
5328
+ if @multiple_object.empty?
5329
+ return L__M(:Miscellany, 2)
5330
+ return late__error() # rubocop: disable Lint/UnreachableCode
5331
+ end
5332
+ if @toomany_flag
5333
+ @toomany_flag = false
5334
+ print L__M(:Miscellany, 1)
5335
+ end
5336
+ i = @location
5337
+ for l in @multiple_object
5338
+ return if deadflag != 0
5339
+ return L__M(:Miscellany, 51) if @location != i
5340
+ PronounNotice(l)
5341
+ @second = @results[3]
5342
+ print "#{l}: "
5343
+ if @inp1.nil?
5344
+ @inp1 = l; result = self.begin_action(@action, l, @second, 0); @inp1 = nil
5345
+ else
5346
+ @inp2 = l; result = self.begin_action(@action, @noun, l, 0); @inp2 = nil
5347
+ end
5348
+ print_routine_result(result)
5349
+ end
5350
+ turn__end(result)
5351
+ end
5352
+ # def begin__action
5353
+
5354
+ # --------------------------------------------------------------
5355
+
5356
+ def turn__end(result = nil)
5357
+
5358
+ # No time passes if either (i) the verb was meta, or
5359
+ # (ii) we've only had the implicit take before the "real"
5360
+ # action to follow.
5361
+ # if @notheld_mode then NoteObjectAcquisitions(); throw :continue; end
5362
+ if @notheld_mode then NoteObjectAcquisitions(); return true; end
5363
+ # throw :continue if @meta
5364
+ return true if @meta
5365
+ unless defined?(MULTI_PLAYER)
5366
+ self.end_turn_sequence() if deadflag == 0
5367
+ end
5368
+ result
5369
+ end
5356
5370
 
5357
5371
  def end_turn_sequence
5358
5372
  AdvanceWorldClock()
5359
- return if deadflag
5373
+ return if deadflag != 0
5360
5374
  RunTimersAndDaemons()
5361
- return if deadflag
5375
+ return if deadflag != 0
5362
5376
  RunEachTurnProperties()
5363
- return if deadflag
5377
+ return if deadflag != 0
5364
5378
  TimePasses()
5365
- return if deadflag
5379
+ return if deadflag != 0
5366
5380
  AdjustLight()
5367
- return if deadflag
5381
+ return if deadflag != 0
5368
5382
  NoteObjectAcquisitions()
5369
5383
  end
5370
5384
 
@@ -5381,7 +5395,8 @@ class InformLibrary < Inform::Ephemeral::Object
5381
5395
  end # DEBUG
5382
5396
 
5383
5397
  contextualize
5384
- if (!@verb.nil? && @verb.meta?) || BeforeRoutines(a) == false
5398
+ before = BeforeRoutines(a)
5399
+ if (!@verb.nil? && @verb.meta?) || before == false
5385
5400
  rv = ActionPrimitive(a)
5386
5401
  end
5387
5402
  return rv
@@ -5395,9 +5410,9 @@ class InformLibrary < Inform::Ephemeral::Object
5395
5410
  color :black_on_white
5396
5411
  bgcolor :white
5397
5412
  print "***"
5398
- L__M(:Miscellany, 3) if deadflag == true
5413
+ L__M(:Miscellany, 3) if deadflag != 0
5399
5414
  L__M(:Miscellany, 4) if deadflag == 2
5400
- if deadflag != false && deadflag != true && deadflag != 2
5415
+ if deadflag != 0 && deadflag != 1 && deadflag != 2
5401
5416
  print " "
5402
5417
  DeathMessage()
5403
5418
  print " "
@@ -5415,7 +5430,7 @@ class InformLibrary < Inform::Ephemeral::Object
5415
5430
  end
5416
5431
 
5417
5432
  def AdvanceWorldClock
5418
- return unless defined? SINGLE_PLAYER
5433
+ return if defined?(MULTI_PLAYER)
5419
5434
  @turns += 1
5420
5435
  if !@the_time.nil?
5421
5436
  if @time_rate >= 0 then @the_time = @the_time + @time_rate
@@ -5431,10 +5446,10 @@ class InformLibrary < Inform::Ephemeral::Object
5431
5446
  end
5432
5447
 
5433
5448
  def RunTimersAndDaemons
5434
- return if !defined?(SINGLE_PLAYER)
5449
+ return if defined?(MULTI_PLAYER)
5435
5450
  if defined?(DEBUG)
5436
5451
  if (@debug_flag & 4) != 0
5437
- @active_timers.each do |the_timer|
5452
+ @the_timers.each do |the_timer|
5438
5453
  next if the_timer.nil?
5439
5454
  print name(the_timer & !WORD_HIGHBIT) + ": "
5440
5455
  if (the_timer & WORD_HIGHBIT) != 0 then print "daemon"
@@ -5446,21 +5461,21 @@ class InformLibrary < Inform::Ephemeral::Object
5446
5461
  end
5447
5462
  end # DEBUG
5448
5463
 
5449
- @active_timers.each do |the_timer|
5464
+ @the_timers.each do |the_timer|
5450
5465
  break if deadflag != 0
5451
5466
  next if the_timer.nil?
5452
- if (the_timer & WORD_HIGHBIT) != 0 then RunRoutines(the_timer & !WORD_HIGHBIT, daemon)
5453
- elsif the_timer.time_left == 0
5467
+ if the_timer.respond_to?(:daemon) then RunRoutines(the_timer, :daemon)
5468
+ elsif (the_timer.&(:time_left) || nil) == 0
5454
5469
  StopTimer(the_timer)
5455
- RunRoutines(the_timer, time_out)
5470
+ RunRoutines(the_timer, :time_out)
5456
5471
  else
5457
- the_timer.time_left = the_timer.time_left - 1
5472
+ the_timer.&(:time_left, the_timer.|(:time_left, 0) - 1)
5458
5473
  end
5459
5474
  end
5460
5475
  end
5461
5476
 
5462
5477
  def RunEachTurnProperties
5463
- return unless defined? SINGLE_PLAYER
5478
+ return if defined?(MULTI_PLAYER)
5464
5479
  @scope_reason = scope_reason = EACH_TURN_REASON; @verb_word = nil
5465
5480
  DoScopeAction(@location, scope_reason)
5466
5481
  SearchScope(ScopeCeiling(@player), @player, nil)
@@ -5477,7 +5492,7 @@ class InformLibrary < Inform::Ephemeral::Object
5477
5492
  end
5478
5493
 
5479
5494
  def AfterGameOver
5480
- return unless defined? SINGLE_PLAYER
5495
+ return if defined?(MULTI_PLAYER)
5481
5496
  prompt
5482
5497
  @temp_global = 0
5483
5498
  i = @parse[1]
@@ -5506,13 +5521,15 @@ class InformLibrary < Inform::Ephemeral::Object
5506
5521
 
5507
5522
  def R_Process(a, i, j)
5508
5523
  s1 = @inp1; s2 = @inp2
5509
- @inp1 = i; @inp2 = j; InformLibrary.begin_action(a, i, j, 1)
5524
+ @inp1 = i; @inp2 = j; result = self.begin_action(a, i, j, 1)
5525
+ print_routine_result(result)
5526
+ ensure
5510
5527
  @inp1 = s1; @inp2 = s2
5511
5528
  end
5512
5529
 
5513
5530
  def NoteObjectAcquisitions
5514
- return unless defined? SINGLE_PLAYER
5515
- player.objectloop.each do |i|
5531
+ return if defined?(MULTI_PLAYER)
5532
+ @player.objectloop do |i|
5516
5533
  if i.hasnt?(:moved)
5517
5534
  give i, :moved
5518
5535
  if i.has?(:scored)
@@ -5540,7 +5557,7 @@ class InformLibrary < Inform::Ephemeral::Object
5540
5557
  def LoopOverScope(routine, act)
5541
5558
  p1 = @parser_one; sr = @scope_reason; a = @actor; al = @actors_location
5542
5559
  @parser_one = routine;
5543
- if act.nil? then @actor = act else @actor = player end
5560
+ if act.nil? then @actor = act else @actor = @player end
5544
5561
  @actors_location = ScopeCeiling(@actor)
5545
5562
  @scope_reason = scope_reason = LOOPOVERSCOPE_REASON
5546
5563
  SearchScope(@actors_location, @actor, scope_reason)
@@ -5581,24 +5598,27 @@ class InformLibrary < Inform::Ephemeral::Object
5581
5598
 
5582
5599
  def ZRegion(z)
5583
5600
  return 0 if z.nil?
5584
- case z.class # Left over from Inform 5
5601
+ return 1 if z.object?
5602
+ case z # Left over from Inform 5
5585
5603
  # when NilClass then 0
5586
- when Object, Class then 1
5604
+ when Class then 1
5587
5605
  when Symbol then 2
5588
5606
  when String then 3
5589
5607
  end
5590
5608
  end
5591
5609
 
5592
- def PrintOrRun(obj, prop, flag = 0)
5610
+ def PrintOrRun(obj, prop, flag = false)
5593
5611
  si = obj.inflib; obj.inflib = inflib
5594
- return RunRoutines(obj, prop) if obj.properties.include?(prop)
5612
+ # return RunRoutines(obj, prop) if obj.properties.include?(prop)
5613
+ return RunRoutines(obj, prop) if obj.properties[prop].is_a?(Array)
5595
5614
  if obj.respond_to?(prop) && !obj.method(prop).nil?
5596
5615
  result = obj.send(prop)
5597
5616
  if result.is_a?(Class) || result.nil? || obj.linked?(prop)
5598
5617
  return RunTimeError(2, obj, prop)
5599
5618
  elsif result.is_a?(String)
5600
5619
  print result.to_s
5601
- new_line if flag == 0
5620
+ raise "Why is flag true?" if flag == true
5621
+ new_line if flag == false
5602
5622
  return true
5603
5623
  else
5604
5624
  return result
@@ -5610,12 +5630,12 @@ class InformLibrary < Inform::Ephemeral::Object
5610
5630
  obj.inflib = si
5611
5631
  end
5612
5632
 
5613
- def PrintOrRunVar(var, flag)
5633
+ def PrintOrRunVar(var, flag = false)
5614
5634
  if var.respond_to?(:name)
5615
5635
  print var.name
5616
5636
  elsif var.is_a?(String)
5617
5637
  print var
5618
- new_line if flag == 0
5638
+ new_line if flag == false
5619
5639
  elsif var.respond_to?(:call)
5620
5640
  return var.call
5621
5641
  else
@@ -5625,15 +5645,77 @@ class InformLibrary < Inform::Ephemeral::Object
5625
5645
  end
5626
5646
 
5627
5647
  def ValueOrRun(obj, prop)
5628
- value = obj.respond_to?(prop) ? obj.send(prop) : RunRoutines(obj, prop)
5629
- return value.number? ? value.to_i : value
5648
+ if obj.properties.key?(prop)
5649
+ value = obj.&(prop)
5650
+ return value.number? ? value.to_i : value
5651
+ elsif obj.respond_to?(prop)
5652
+ value = obj.send(prop)
5653
+ return value.number? ? value.to_i : value
5654
+ end
5655
+ return RunRoutines(obj, prop)
5630
5656
  end
5631
5657
 
5632
5658
  def RunRoutines(obj, prop)
5633
5659
  obj = @real_location if obj == thedark && prop.notin?(:initial, :short_name, :description, :life)
5634
5660
  return false if prop.nil?
5635
- val = obj.&(prop); return val unless val.nil?
5636
- return reacts_to?(obj, prop)
5661
+
5662
+ value = obj.&(prop)
5663
+ return print_routine_result(value) unless value.nil?
5664
+
5665
+ reacts_to?(obj, prop)
5666
+ end
5667
+
5668
+ # ----------------------------------------------------------------------------
5669
+
5670
+ def StartTimer(obj, timer)
5671
+ return false if @the_timers.include?(obj)
5672
+ if @the_timers.length >= MAX_TIMERS
5673
+ RunTimeError(4)
5674
+ return
5675
+ end
5676
+ if obj.&(:time_left) == 0
5677
+ RunTimeError(5,obj)
5678
+ return
5679
+ end
5680
+ @the_timers << obj
5681
+ obj.&(:time_left, timer)
5682
+ end
5683
+
5684
+ def StopTimer(obj)
5685
+ return false unless @the_timers.include?(obj)
5686
+ if obj.&(:time_left) == 0
5687
+ RunTimeError(5,obj)
5688
+ return
5689
+ end
5690
+ @the_timers.delete(obj)
5691
+ obj.&(:time_left, 0)
5692
+ end
5693
+
5694
+ def StartDaemon(obj)
5695
+ return false if @the_timers.include?(obj)
5696
+ RunTimeError(4) if @the_timers.length >= MAX_TIMERS
5697
+ @the_timers << obj
5698
+ end
5699
+
5700
+ def StopDaemon(obj)
5701
+ return false unless @the_timers.include?(obj)
5702
+ @the_timers.delete(obj)
5703
+ end
5704
+
5705
+ # ----------------------------------------------------------------------------
5706
+
5707
+ def DisplayStatus
5708
+ if @sys_statusline_flag == 0 then @sline1 = @score; @sline2 = @turns
5709
+ else @sline1 = @the_time/60; @sline2 = @the_time%60; end
5710
+ end
5711
+
5712
+ def SetTime(t, s)
5713
+ @the_time = t; @time_rate = s; @time_step = 0
5714
+ @time_step = 0 - s if s < 0
5715
+ end
5716
+
5717
+ def NotifyTheScore
5718
+ print "\n["; L__M(:Miscellany, 50, @score - @last_score); print ".]\n"
5637
5719
  end
5638
5720
  # ----------------------------------------------------------------------------
5639
5721
  end