Olib 0.0.8 → 0.0.9

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.
@@ -1,91 +1,91 @@
1
- module Olib
2
- module Errors
3
- @@padding = 20
4
-
5
- def Errors.padding(n)
6
- @@padding = n
7
- end
8
-
9
- def Errors.indent(str)
10
- str.split("\n").map { |line| [''] * @@padding * " " + line }.join("\n")
11
- end
12
-
13
- # used internally to handle early thread execution and error bubbling
14
- class Mundane < Exception
15
- end
16
-
17
- # used internally to insure threads died and error bubbling
18
- class TimedOut < Exception
19
- end
20
-
21
- # used internally to handle early thread execution and error bubbling
22
- class Prempt < Exception
23
- end
24
-
25
- # used to exit a thread early due to an error that Olib cannot resolve internally
26
- class Fatal < Exception
27
- def initialize(message=nil)
28
- unless message
29
- message = String.new
30
- message.concat "\n\nAn Olib::Errors::Fatal was raised but not rescued in an Olib method"
31
- message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
32
- end
33
- super Errors.indent message
34
- end
35
- end
36
-
37
- # If you have a full container and a script/action depends on it, this is thrown
38
- # rescue it for logic
39
- class ContainerFull < Exception
40
- def initialize(message=nil)
41
- unless message
42
- message = String.new
43
- message.concat "\n\nYou tried to add stuff to a container that was full"
44
- message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
45
- end
46
- super Errors.indent message
47
- end
48
- end
49
-
50
- class InsufficientFunds < Exception
51
- def initialize(message=nil)
52
- unless message
53
- message = String.new
54
- message.concat "\n\nYou tried to do something that costs more money that your character had on them"
55
- message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
56
- end
57
- super Errors.indent message
58
- end
59
- end
60
-
61
- class HandsFull < Exception
62
- def initialize
63
- message = String.new
64
- message.concat "\n\nYour hands were full!"
65
- message.concat "\nyou should rescue this error if you don't want your script to break"
66
- super Errors.indent message
67
- end
68
- end
69
-
70
- class TooHeavy < Exception
71
- def initialize
72
- message = String.new
73
- message.concat "\n\nYou were too heavy to do something!"
74
- message.concat "\nyou should rescue this error if you don't want your script to break"
75
- super Errors.indent message
76
- end
77
- end
78
-
79
- class DoesntExist < Exception
80
- def initialize(message=nil)
81
- unless message
82
- message = String.new
83
- message.concat "\n\nYou tried to interact with something that no longer exists"
84
- message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
85
- end
86
- super Errors.indent message
87
- end
88
- end
89
-
90
- end
91
- end
1
+ module Olib
2
+ module Errors
3
+ @@padding = 20
4
+
5
+ def Errors.padding(n)
6
+ @@padding = n
7
+ end
8
+
9
+ def Errors.indent(str)
10
+ str.split("\n").map { |line| [''] * @@padding * " " + line }.join("\n")
11
+ end
12
+
13
+ # used internally to handle early thread execution and error bubbling
14
+ class Mundane < Exception
15
+ end
16
+
17
+ # used internally to insure threads died and error bubbling
18
+ class TimedOut < Exception
19
+ end
20
+
21
+ # used internally to handle early thread execution and error bubbling
22
+ class Prempt < Exception
23
+ end
24
+
25
+ # used to exit a thread early due to an error that Olib cannot resolve internally
26
+ class Fatal < Exception
27
+ def initialize(message=nil)
28
+ unless message
29
+ message = String.new
30
+ message.concat "\n\nAn Olib::Errors::Fatal was raised but not rescued in an Olib method"
31
+ message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
32
+ end
33
+ super Errors.indent message
34
+ end
35
+ end
36
+
37
+ # If you have a full container and a script/action depends on it, this is thrown
38
+ # rescue it for logic
39
+ class ContainerFull < Exception
40
+ def initialize(message=nil)
41
+ unless message
42
+ message = String.new
43
+ message.concat "\n\nYou tried to add stuff to a container that was full"
44
+ message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
45
+ end
46
+ super Errors.indent message
47
+ end
48
+ end
49
+
50
+ class InsufficientFunds < Exception
51
+ def initialize(message=nil)
52
+ unless message
53
+ message = String.new
54
+ message.concat "\n\nYou tried to do something that costs more money that your character had on them"
55
+ message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
56
+ end
57
+ super Errors.indent message
58
+ end
59
+ end
60
+
61
+ class HandsFull < Exception
62
+ def initialize
63
+ message = String.new
64
+ message.concat "\n\nYour hands were full!"
65
+ message.concat "\nyou should rescue this error if you don't want your script to break"
66
+ super Errors.indent message
67
+ end
68
+ end
69
+
70
+ class TooHeavy < Exception
71
+ def initialize
72
+ message = String.new
73
+ message.concat "\n\nYou were too heavy to do something!"
74
+ message.concat "\nyou should rescue this error if you don't want your script to break"
75
+ super Errors.indent message
76
+ end
77
+ end
78
+
79
+ class DoesntExist < Exception
80
+ def initialize(message=nil)
81
+ unless message
82
+ message = String.new
83
+ message.concat "\n\nYou tried to interact with something that no longer exists"
84
+ message.concat "\nyou should rescue this error if it isn't fatal and you don't want your script to break"
85
+ end
86
+ super Errors.indent message
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -1,20 +1,28 @@
1
- # used to wrap and extend a GameObj item
2
- module Olib
3
- class Gameobj_Extender
4
- attr_accessor :type
5
- def initialize(item)
6
- self.__extend__(item)
7
- @type = item.type
8
- end
9
-
10
- # This copies GameObj data to attributes so we can employ it for scripting
11
- def __extend__(item)
12
- item.instance_variables.each { |var|
13
- s = var.to_s.sub('@', '')
14
- (class << self; self end).class_eval do; attr_accessor "#{s}"; end
15
- instance_variable_set "#{var}", item.send(s)
16
- }
17
-
18
- end
19
- end
1
+ # used to wrap and extend a GameObj item
2
+ module Olib
3
+ class Gameobj_Extender
4
+ attr_accessor :type
5
+ def initialize(item)
6
+ self.__extend__(item)
7
+ @type = item.type
8
+ end
9
+
10
+ # This copies GameObj data to attributes so we can employ it for scripting
11
+ def __extend__(item)
12
+ item.instance_variables.each { |var|
13
+ s = var.to_s.sub('@', '')
14
+ (class << self; self end).class_eval do; attr_accessor "#{s}"; end
15
+ instance_variable_set "#{var}", item.send(s)
16
+ }
17
+ end
18
+
19
+ def echo
20
+ respond self
21
+ self
22
+ end
23
+
24
+ def at
25
+ fput "look at ##{@id}"
26
+ end
27
+ end
20
28
  end
@@ -1,550 +1,550 @@
1
- require 'Olib/core/extender'
2
-
3
- module Olib
4
- # this is the structure for a base Object
5
- # wraps an instance of GameObj and adds the ability for tags, queries
6
- class Item < Olib::Gameobj_Extender
7
- attr_accessor :props
8
- # When created, it should be passed an instance of GameObj
9
- #
10
- # Example:
11
- # Olib::Item.new(GameObj.right_hand)
12
- def initialize(obj)
13
- @props = Hash.new
14
-
15
- @props[:name] = obj.name
16
- @props[:after_name] = obj.after_name
17
- @props[:before_name]= obj.before_name
18
- @props[:desc] = [obj.before_name, obj.name, obj.after_name].compact.join(' ')
19
-
20
- define :tags, []
21
- obj.type.split(',').map { |t| tag(t) }
22
-
23
- if is?('jar') && @props[:after_name] =~ /containing (.*+?)/
24
- tag Dictionary.gems[:singularize].call @props[:after_name].gsub('containing', '').strip
25
- end
26
-
27
- if is?('gem')
28
- tag Dictionary.gems[:singularize].call @props[:name]
29
- end
30
-
31
- if is?('jar') && @props[:after_name].nil?
32
- tag('empty')
33
- end
34
-
35
- if Vars.teleporter && Vars.teleporter == @props[:name]
36
- tag('teleporter')
37
- end
38
-
39
- super(obj)
40
- end
41
-
42
- def to_s
43
- @props.to_s
44
- end
45
-
46
- # determine if Item is something
47
- #
48
- # example:
49
- # item.is?("jar")
50
- #
51
- def is?(tag)
52
- @props[:tags].include?(tag)
53
- end
54
-
55
- def tag(*tags)
56
- @props[:tags].concat(tags)
57
- self
58
- end
59
-
60
- def untag(*remove)
61
- @props[:tags] = @props[:tags].select { |tag| !remove.include?(tag) }
62
- end
63
-
64
- def tags
65
- @props[:tags]
66
- end
67
-
68
- def worn?
69
- GameObj.inv.collect { |item| item.id }.include? @id
70
- end
71
-
72
- def crawl
73
- take
74
- tap._inspect.read.look
75
- self
76
- end
77
-
78
- def buy
79
- Char.deplete_wealth cost
80
- fput action 'buy'
81
- self
82
- end
83
-
84
- def has?(key)
85
- !@props[key].nil?
86
- end
87
-
88
- def missing?(key)
89
- !has?(key)
90
- end
91
-
92
- def exists?
93
- GameObj[@id].nil?
94
- end
95
-
96
- def pullable?
97
- is? 'pullable'
98
- end
99
-
100
- def affordable?
101
- take
102
- return true if pullable?
103
- cost <= Char.smart_wealth
104
- end
105
-
106
- def buyable?
107
- is? 'buyable'
108
- end
109
-
110
- def cost
111
- @props['cost']
112
- end
113
-
114
- def acquire_from_shop
115
- take
116
- if pullable?
117
- return pull
118
- else
119
- Olib.wrap(action "buy"){ |line|
120
- raise Olib::Errors::InsufficientFunds if line =~ /The merchant frowns/
121
- if line =~ /You hand over/
122
- Char.deplete_wealth cost
123
- raise Olib::Errors::Mundane
124
- end
125
- }
126
- end
127
- self
128
- end
129
-
130
- def in
131
- return self if has? 'contents'
132
- Olib.wrap(action 'look in') { |line|
133
- raise Olib::Errors::Mundane if line=~/^There is nothing in there./
134
-
135
- # handle jar data
136
- if line =~ /Inside (.*?) you see (?<number>[\d]+) portion(|s) of (?<type>.*?). It is (more than |less than|)(?<percent>[a-z ]+)./
137
- data = line.match(/Inside (.*?) you see (?<number>[\d]+) portion(|s) of (?<type>.*?). It is (more than |less than|)(?<percentage>[a-z ]+)./)
138
- tag data[:percentage] == 'full' ? "full" : "partial"
139
- define :number, data[:number].to_i
140
- raise Olib::Errors::Mundane
141
- end
142
-
143
- #handle empty jars
144
- if line =~ /The (.*?) is empty./
145
- tag 'empty'
146
- raise Olib::Errors::Mundane
147
- end
148
- }
149
- self
150
- end
151
-
152
- def _drag(target)
153
- Olib.wrap("_drag ##{@id} ##{target.id}") { |line|
154
- # order of operations is important here for jars
155
- raise Olib::Errors::DoesntExist if line =~ Dictionary.put[:failure][:ne]
156
- raise Olib::Errors::Mundane if line =~ Dictionary.put[:success]
157
-
158
- if line =~ Dictionary.put[:failure][:full]
159
- tag 'full'
160
- raise Olib::Errors::ContainerFull
161
- end
162
- }
163
- self
164
- end
165
-
166
- def stash
167
- _drag GameObj[props['container']]
168
- end
169
-
170
- def shake
171
- # make sure we have a count so we need to match fewer lines
172
- self.in if is? 'jar' and missing? :number
173
-
174
- Olib.wrap(action "shake"){ |line|
175
- raise Olib::Errors::Fatal if line =~ /you realize that it is empty/
176
- if line =~ /fall into your/
177
- @props[:number] = @props[:number]-1
178
- raise Olib::Errors::Fatal.new "Jar is now empty\n you should rescue this to handle it gracefully" if @props[:number] == 0
179
- raise Olib::Errors::Mundane
180
- end
181
- }
182
- self
183
- end
184
-
185
- def shop_sell(amt)
186
- if GameObj.right_hand.id != @id
187
- raise Olib::Errors::Fatal
188
- end
189
-
190
- Olib.wrap("shop sell #{amt}") {|line|
191
- raise Olib::Errors::Mundane if line =~ /^You place your/
192
- raise Olib::Errors::Fatal if line =~ /There's no more room for anything else right now./
193
- }
194
-
195
- end
196
-
197
- def sell
198
- price = 0
199
- Olib.wrap( action "sell" ){ |line|
200
- raise Olib::Errors::Fatal.new "#{to_s} is not sellable here" if GameObj.right_hand.id == @id
201
-
202
- if line =~ /([\d]+) silver/
203
- price = $1.to_i
204
- raise Olib::Errors::Mundane
205
- end
206
- }
207
- end
208
-
209
- def turn
210
- fput action 'turn'
211
- self
212
- end
213
-
214
- def action(verb)
215
- "#{verb} ##{@id}"
216
- end
217
-
218
- def add(*items)
219
- items.each { |item|
220
- item._drag(self)
221
- }
222
- self
223
- end
224
-
225
- def define(key, val)
226
- @props[key] = val
227
- self
228
- end
229
-
230
- def pull(onfailure=nil)
231
- refresh_instance = false
232
- original_right = GameObj.right_hand
233
- original_left = GameObj.left_hand
234
- Olib.wrap(action "pull") { |line|
235
-
236
- if line =~ /^You pull/
237
- if line =~ /There (are|is) ([\d]+) left/
238
- refresh_instance = true
239
- end
240
- raise Olib::Errors::Mundane
241
- end
242
-
243
- if line =~ /I'm afraid that you can't pull that./
244
- if onfailure
245
- onfailure.call(self)
246
- else
247
- raise Olib::Errors::DoesntExist
248
- end
249
- end
250
- }
251
- # for stacked items in shops
252
- if refresh_instance
253
- return Item.new(GameObj.left_hand) if original_left.nil? && !GameObj.left_hand.nil?
254
- return Item.new(GameObj.right_hand)
255
- end
256
- self
257
- end
258
-
259
- def give(target, onfailure=nil)
260
- Olib.wrap_stream("give ##{@id} to #{target}", 30) { |line|
261
- raise Olib::Errors::Mundane if line=~ /This looks perfect/
262
-
263
- if line =~ /glances at you and moves out of your reach./
264
- raise Olib::Errors::Fatal.new "You appear to have an invalid NPC definition"
265
- end
266
-
267
- }
268
-
269
- self
270
- end
271
-
272
- def remove(onfailure=nil)
273
-
274
- unless GameObj.inv.map(&:id).include? @id
275
- if onfailure
276
- onfailure.call(self)
277
- else
278
- raise Olib::Errors::DoesntExist
279
- end
280
- end
281
-
282
- Olib.wrap(action "remove") { |line|
283
- if line =~ /You cannot remove|You better get a sharp knife/
284
- if onfailure
285
- onfailure.call(self)
286
- else
287
- raise Olib::Errors::DoesntExist
288
- end
289
- end
290
-
291
- raise Olib::Errors::Mundane if GameObj.right_hand.id == @id || GameObj.left_hand.id == @id
292
- }
293
-
294
- self
295
- end
296
-
297
- def wear(onfailure=nil)
298
- if GameObj.right_hand.id != @id || GameObj.left_hand.id != @id
299
- if onfailure
300
- onfailure.call(self)
301
- else
302
- raise Olib::Errors::DoesntExist
303
- end
304
- end
305
-
306
- Olib.wrap(action "wear") { |line|
307
- if line =~ /You can't wear that.|You can only wear/
308
- if onfailure
309
- onfailure.call(self)
310
- else
311
- raise Olib::Errors::DoesntExist
312
- end
313
- end
314
- raise Olib::Errors::Mundane if GameObj.inv.map(&:id).include? @id
315
- }
316
-
317
- self
318
-
319
- end
320
-
321
- def analyze
322
- fput "analyze ##{@id}"
323
- should_detect = false
324
- begin
325
- Timeout::timeout(1) do
326
- while(line = get)
327
- next if Olib::Dictionary.ignorable?(line)
328
- next if line =~ /sense that the item is free from merchant alteration restrictions|and sense that the item is largely free from merchant alteration restrictions|these can all be altered by a skilled merchant|please keep the messaging in mind when designing an alterations|there is no recorded information on that item|The creator has also provided the following information/
329
- @props['max_light'] = true if line =~ /light as it can get/
330
- @props['max_deep'] = true if line =~ /pockets could not possibly get any deeper/
331
- @props['max_deep'] = false if line =~ /pockets deepened/
332
- @props['max_light'] = false if line =~ /talented merchant lighten/
333
- if line =~ /Casting Elemental Detection/
334
- should_detect = true
335
- next
336
- end
337
- break if line =~ /pockets deepened|^You get no sense of whether|light as it can get|pockets could not possibly get any deeper|talented merchant lighten/
338
- @props['analyze'] = String.new unless @props['analyze']
339
- @props['analyze'].concat line.strip
340
- @props['analyze'].concat " "
341
- end
342
- end
343
-
344
- rescue Timeout::Error
345
- # Silent
346
- end
347
- detect if should_detect
348
- temp_analysis = @props['analyze'].split('.').map(&:strip).map(&:downcase).reject {|ln| ln.empty? }
349
- @props['analyze'] = temp_analysis unless temp_analysis.empty?
350
- return self
351
- end
352
-
353
- def take
354
- return self if has? 'cost'
355
-
356
- Olib.wrap(action 'get') { |line|
357
- raise Errors::DoesntExist if line=~ Olib::Dictionary.get[:failure][:ne]
358
- raise Errors::HandsFull if line=~ Olib::Dictionary.get[:failure][:hands_full]
359
- raise Errors::TooHeavy if line=~ Olib::Dictionary.get[:failure][:weight]
360
-
361
- if line=~ Olib::Dictionary.get[:success]
362
- raise Olib::Errors::Mundane
363
- end
364
-
365
- if line =~ Olib::Dictionary.get[:failure][:buy]
366
- define 'cost', line.match(Olib::Dictionary.get[:failure][:buy])[:cost].to_i
367
- end
368
-
369
- if line =~ Olib::Dictionary.get[:failure][:race]
370
- define 'cost', line.match(Olib::Dictionary.get[:failure][:race])[:cost].to_i
371
- end
372
-
373
- if line =~ /let me help you with that/
374
- raise Olib::Errors::Mundane
375
- end
376
-
377
- if line=~ /You'll have to buy it if you want it/
378
- tag 'buyable'
379
- raise Olib::Errors::Mundane
380
- end
381
-
382
- if line=~ /You can PULL/
383
- tag 'pullable'
384
- raise Olib::Errors::Mundane
385
- end
386
-
387
- }
388
- self
389
- end
390
-
391
- def drop
392
- Script.log("#{Time.now} > dropped #{to_s}")
393
- fput action "drop"
394
- self
395
- end
396
-
397
- def _inspect
398
-
399
- return self if has? 'inspect'
400
-
401
- in_inspect = false
402
-
403
- Olib.wrap_stream(action 'inspect') { |line|
404
-
405
- raise Olib::Errors::Mundane if line =~ /^<prompt/ and in_inspect
406
-
407
- # skip first inspect line because it's useless for info
408
- if line =~ /You carefully inspect|You carefully count|goat/
409
- in_inspect = true
410
- end
411
-
412
-
413
- if in_inspect
414
-
415
- if line =~ /^You estimate that (?:.*?) can store (?:a|an|some) ([a-zA-Z -]+) amount with enough space for ([a-zA-Z ]+)/
416
- @props['space'] = $1
417
- @props['number_of_items'] = $2
418
- end
419
-
420
-
421
-
422
- if line =~ /^You determine that you could wear the (.*?) ([a-zA-Z ]+)/
423
- @props['location']= $2
424
- end
425
-
426
- if line =~ /allows you to conclude that it is ([a-zA-Z ]+)/
427
-
428
- if line =~ Dictionary.size
429
- @props['shield_type'] = $1
430
- else
431
- Dictionary.armors.each do |type, re| @props['armor_type'] = type if line =~ re end
432
- end
433
-
434
- end
435
-
436
- if line =~ /suitable for use in unarmed combat/
437
- @props['weapon_type']= "uac"
438
- end
439
-
440
- if line =~ /requires skill in ([a-zA-Z ]+) to use effectively/
441
-
442
- @props['weapon_type']= $1
443
- if line =~ /It appears to be a modified ([a-zA-Z -]+)/
444
- @props['weapon_base']= $1
445
- else
446
- @props['weapon_base']= @noun
447
- end
448
- end
449
-
450
- if line =~ /^It looks like this item has been mainly crafted out of ([a-zA-Z -]+)./
451
- @props['material']= $1
452
- raise Olib::Errors::Mundane
453
- end
454
-
455
- if line =~ /can hold liquids/
456
- @props['liquid_container']=true
457
- end
458
-
459
- end
460
-
461
- }
462
-
463
- return self
464
- end
465
-
466
- def look
467
- return self if has? 'show'
468
- Olib.wrap(action 'look') { |line|
469
- raise Olib::Errors::Mundane if line=~/^You see nothing unusual.|^You can't quite get a good look at/
470
- define 'show', line unless line=~/<prompt|You take a closer look/
471
- }
472
- self
473
- end
474
-
475
- def tap
476
- return self if has? 'description'
477
- Olib.wrap(action 'tap') { |line|
478
- next unless line=~ /You tap (.*?) (on|in)/
479
- define 'description', $1
480
- raise Olib::Errors::Mundane
481
- }
482
- self
483
- end
484
-
485
- def price
486
- return self if(has? 'price' or has? 'info')
487
- Olib.wrap(action 'get') { |line|
488
-
489
- if line =~ /(\d+) silvers/
490
- define 'price', line.match(/(?<price>\d+) silvers/)[:price]
491
- raise Olib::Errors::Mundane
492
- end
493
-
494
- if line =~ /You can't pick that up/
495
- define "info", true
496
- raise Olib::Errors::Mundane
497
- end
498
-
499
- Script.log "unmatched price: #{line}"
500
-
501
- }
502
- self
503
- end
504
-
505
- def read
506
- return self if has? 'read'
507
- scroll = false
508
- multiline = false
509
- Olib.wrap_stream(action 'read') { |line|
510
-
511
- raise Olib::Errors::Mundane if line =~ /^<prompt/ and (multiline or scroll)
512
- raise Olib::Errors::Mundane if line =~ /There is nothing there to read|You can't do that./
513
-
514
- # if we are in a multiline state
515
- @props['read'] = @props['read'].concat line if multiline
516
-
517
- # capture spell
518
- if scroll && line =~ /\(([0-9]+)\) ([a-zA-Z'\s]+)/
519
- n = $1
520
- name = $2
521
- spell = {'n' => $1, 'name' => $2}
522
- #Client.notify "Spell detected ... (#{$1}) #{$2}"
523
- @props['spells'].push spell
524
-
525
- # begin scroll
526
- elsif line =~ /It takes you a moment to focus on the/
527
- scroll = true
528
- @props['spells'] = Array.new
529
-
530
- # open multiline
531
- elsif line =~ /^In the (.*?) language, it reads/
532
- multiline = true
533
- @props['read'] = "#{line}\n"
534
- @props['language'] = $1
535
-
536
- # alert to unknown
537
- elsif line =~ /but the language is not one you know. It looks like it's written in (.*?)./
538
- Script.log "Please find a friend that can read for #{$1} in #{XMLData.room_title}"
539
- echo "Please find a friend that can read for #{$1} in #{XMLData.room_title}"
540
- raise Olib::Errors::Mundane
541
-
542
- end
543
-
544
- }
545
- return self
546
- end
547
-
548
- end
549
-
1
+ require 'Olib/core/extender'
2
+
3
+ module Olib
4
+ # this is the structure for a base Object
5
+ # wraps an instance of GameObj and adds the ability for tags, queries
6
+ class Item < Olib::Gameobj_Extender
7
+ attr_accessor :props, :container
8
+ # When created, it should be passed an instance of GameObj
9
+ #
10
+ # Example:
11
+ # Olib::Item.new(GameObj.right_hand)
12
+ def initialize(obj)
13
+ @props = Hash.new
14
+ @props[:name] = obj.name
15
+ @props[:after_name] = obj.after_name
16
+ @props[:before_name]= obj.before_name
17
+ @props[:desc] = [obj.before_name, obj.name, obj.after_name].compact.join(' ')
18
+ @props[:noun] = obj.noun
19
+ define :tags, []
20
+ obj.type.split(',').map { |t| tag(t) }
21
+
22
+ if is?('jar') && @props[:after_name] =~ /containing (.*+?)/
23
+ tag Dictionary.gems[:singularize].call @props[:after_name].gsub('containing', '').strip
24
+ end
25
+
26
+ if is?('gem')
27
+ tag Dictionary.gems[:singularize].call @props[:name]
28
+ end
29
+
30
+ if is?('jar') && @props[:after_name].nil?
31
+ tag('empty')
32
+ end
33
+
34
+ if Vars.teleporter && Vars.teleporter == @props[:name]
35
+ tag('teleporter')
36
+ end
37
+
38
+ super(obj)
39
+ end
40
+
41
+ def to_s
42
+ @props.to_s
43
+ end
44
+
45
+ # determine if Item is something
46
+ #
47
+ # example:
48
+ # item.is?("jar")
49
+ #
50
+ def is?(tag)
51
+ @props[:tags].include?(tag)
52
+ end
53
+
54
+ def tag(*tags)
55
+ @props[:tags].concat(tags)
56
+ self
57
+ end
58
+
59
+ def untag(*remove)
60
+ @props[:tags] = @props[:tags].select { |tag| !remove.include?(tag) }
61
+ end
62
+
63
+ def tags
64
+ @props[:tags]
65
+ end
66
+
67
+ def worn?
68
+ GameObj.inv.collect { |item| item.id }.include? @id
69
+ end
70
+
71
+ def crawl
72
+ take
73
+ tap._inspect.read.look
74
+ self
75
+ end
76
+
77
+ def buy
78
+ Char.deplete_wealth cost
79
+ fput action 'buy'
80
+ self
81
+ end
82
+
83
+ def has?(key)
84
+ !@props[key].nil?
85
+ end
86
+
87
+ def missing?(key)
88
+ !has?(key)
89
+ end
90
+
91
+ def exists?
92
+ GameObj[@id].nil?
93
+ end
94
+
95
+ def pullable?
96
+ is? 'pullable'
97
+ end
98
+
99
+ def affordable?
100
+ take
101
+ return true if pullable?
102
+ cost <= Char.smart_wealth
103
+ end
104
+
105
+ def buyable?
106
+ is? 'buyable'
107
+ end
108
+
109
+ def cost
110
+ @props['cost']
111
+ end
112
+
113
+ def acquire_from_shop
114
+ take
115
+ if pullable?
116
+ return pull
117
+ else
118
+ Olib.wrap(action "buy"){ |line|
119
+ raise Olib::Errors::InsufficientFunds if line =~ /The merchant frowns/
120
+ if line =~ /You hand over/
121
+ Char.deplete_wealth cost
122
+ raise Olib::Errors::Mundane
123
+ end
124
+ }
125
+ end
126
+ self
127
+ end
128
+
129
+ def in
130
+ return self if has? 'contents'
131
+ Olib.wrap(action 'look in') { |line|
132
+ if line=~/^There is nothing in there.|You gaze through (.*?) and see.../
133
+ raise Olib::Errors::Mundane
134
+ end
135
+
136
+ # handle jar data
137
+ if line =~ /Inside (.*?) you see (?<number>[\d]+) portion(|s) of (?<type>.*?). It is (more than |less than|)(?<percent>[a-z ]+)./
138
+ data = line.match(/Inside (.*?) you see (?<number>[\d]+) portion(|s) of (?<type>.*?). It is (more than |less than|)(?<percentage>[a-z ]+)./)
139
+ tag data[:percentage] == 'full' ? "full" : "partial"
140
+ define :number, data[:number].to_i
141
+ raise Olib::Errors::Mundane
142
+ end
143
+
144
+ #handle empty jars
145
+ if line =~ /The (.*?) is empty./
146
+ tag 'empty'
147
+ raise Olib::Errors::Mundane
148
+ end
149
+ }
150
+ self
151
+ end
152
+
153
+ def _drag(target)
154
+ Olib.wrap("_drag ##{@id} ##{target.id}") { |line|
155
+ # order of operations is important here for jars
156
+ raise Olib::Errors::DoesntExist if line =~ Dictionary.put[:failure][:ne]
157
+ raise Olib::Errors::Mundane if line =~ Dictionary.put[:success]
158
+
159
+ if line =~ Dictionary.put[:failure][:full]
160
+ tag 'full'
161
+ raise Olib::Errors::ContainerFull
162
+ end
163
+ }
164
+ self
165
+ end
166
+
167
+ def stash
168
+ _drag GameObj[props['container']]
169
+ end
170
+
171
+ def shake
172
+ # make sure we have a count so we need to match fewer lines
173
+ self.in if is? 'jar' and missing? :number
174
+
175
+ Olib.wrap(action "shake"){ |line|
176
+ raise Olib::Errors::Fatal if line =~ /you realize that it is empty/
177
+ if line =~ /fall into your/
178
+ @props[:number] = @props[:number]-1
179
+ raise Olib::Errors::Fatal.new "Jar is now empty\n you should rescue this to handle it gracefully" if @props[:number] == 0
180
+ raise Olib::Errors::Mundane
181
+ end
182
+ }
183
+ self
184
+ end
185
+
186
+ def shop_sell(amt)
187
+ if GameObj.right_hand.id != @id
188
+ raise Olib::Errors::Fatal
189
+ end
190
+
191
+ Olib.wrap("shop sell #{amt}") {|line|
192
+ raise Olib::Errors::Mundane if line =~ /^You place your/
193
+ raise Olib::Errors::Fatal if line =~ /There's no more room for anything else right now./
194
+ }
195
+
196
+ end
197
+
198
+ def sell
199
+ price = 0
200
+ take
201
+ Olib.wrap( action "sell" ){ |line|
202
+ raise Olib::Errors::Fatal.new "#{to_s} is not sellable here" if GameObj.right_hand.id == @id
203
+
204
+ if line =~ /([\d]+) silver/
205
+ price = $1.to_i
206
+ raise Olib::Errors::Mundane
207
+ end
208
+ }
209
+ price
210
+ end
211
+
212
+ def turn
213
+ fput action 'turn'
214
+ self
215
+ end
216
+
217
+ def action(verb)
218
+ "#{verb} ##{@id}"
219
+ end
220
+
221
+ def add(*items)
222
+ items.each { |item|
223
+ item._drag(self)
224
+ }
225
+ self
226
+ end
227
+
228
+ def define(key, val)
229
+ @props[key] = val
230
+ self
231
+ end
232
+
233
+ def pull(onfailure=nil)
234
+ refresh_instance = false
235
+ original_right = GameObj.right_hand
236
+ original_left = GameObj.left_hand
237
+ Olib.wrap(action "pull") { |line|
238
+
239
+ if line =~ /^You pull/
240
+ if line =~ /There (are|is) ([\d]+) left/
241
+ refresh_instance = true
242
+ end
243
+ raise Olib::Errors::Mundane
244
+ end
245
+
246
+ if line =~ /I'm afraid that you can't pull that./
247
+ if onfailure
248
+ onfailure.call(self)
249
+ else
250
+ raise Olib::Errors::DoesntExist
251
+ end
252
+ end
253
+ }
254
+ # for stacked items in shops
255
+ if refresh_instance
256
+ return Item.new(GameObj.left_hand) if original_left.nil? && !GameObj.left_hand.nil?
257
+ return Item.new(GameObj.right_hand)
258
+ end
259
+ self
260
+ end
261
+
262
+ def give(target, onfailure=nil)
263
+ Olib.wrap_stream("give ##{@id} to #{target}", 30) { |line|
264
+ raise Olib::Errors::Mundane if line=~ /This looks perfect/
265
+
266
+ if line =~ /glances at you and moves out of your reach./
267
+ raise Olib::Errors::Fatal.new "You appear to have an invalid NPC definition"
268
+ end
269
+
270
+ }
271
+
272
+ self
273
+ end
274
+
275
+ def remove(onfailure=nil)
276
+
277
+ unless GameObj.inv.map(&:id).include? @id
278
+ if onfailure
279
+ onfailure.call(self)
280
+ else
281
+ raise Olib::Errors::DoesntExist
282
+ end
283
+ end
284
+
285
+ Olib.wrap(action "remove") { |line|
286
+ if line =~ /You cannot remove|You better get a sharp knife/
287
+ if onfailure
288
+ onfailure.call(self)
289
+ else
290
+ raise Olib::Errors::DoesntExist
291
+ end
292
+ end
293
+
294
+ raise Olib::Errors::Mundane if GameObj.right_hand.id == @id || GameObj.left_hand.id == @id
295
+ }
296
+
297
+ self
298
+ end
299
+
300
+ def wear(onfailure=nil)
301
+ if GameObj.right_hand.id != @id || GameObj.left_hand.id != @id
302
+ if onfailure
303
+ onfailure.call(self)
304
+ else
305
+ raise Olib::Errors::DoesntExist
306
+ end
307
+ end
308
+
309
+ Olib.wrap(action "wear") { |line|
310
+ if line =~ /You can't wear that.|You can only wear/
311
+ if onfailure
312
+ onfailure.call(self)
313
+ else
314
+ raise Olib::Errors::DoesntExist
315
+ end
316
+ end
317
+ raise Olib::Errors::Mundane if GameObj.inv.map(&:id).include? @id
318
+ }
319
+
320
+ self
321
+
322
+ end
323
+
324
+ def analyze
325
+ fput "analyze ##{@id}"
326
+ should_detect = false
327
+ begin
328
+ Timeout::timeout(1) do
329
+ while(line = get)
330
+ next if Olib::Dictionary.ignorable?(line)
331
+ next if line =~ /sense that the item is free from merchant alteration restrictions|and sense that the item is largely free from merchant alteration restrictions|these can all be altered by a skilled merchant|please keep the messaging in mind when designing an alterations|there is no recorded information on that item|The creator has also provided the following information/
332
+ @props['max_light'] = true if line =~ /light as it can get/
333
+ @props['max_deep'] = true if line =~ /pockets could not possibly get any deeper/
334
+ @props['max_deep'] = false if line =~ /pockets deepened/
335
+ @props['max_light'] = false if line =~ /talented merchant lighten/
336
+ if line =~ /Casting Elemental Detection/
337
+ should_detect = true
338
+ next
339
+ end
340
+ break if line =~ /pockets deepened|^You get no sense of whether|light as it can get|pockets could not possibly get any deeper|talented merchant lighten/
341
+ @props['analyze'] = String.new unless @props['analyze']
342
+ @props['analyze'].concat line.strip
343
+ @props['analyze'].concat " "
344
+ end
345
+ end
346
+
347
+ rescue Timeout::Error
348
+ # Silent
349
+ end
350
+ detect if should_detect
351
+ temp_analysis = @props['analyze'].split('.').map(&:strip).map(&:downcase).reject {|ln| ln.empty? }
352
+ @props['analyze'] = temp_analysis unless temp_analysis.empty?
353
+ return self
354
+ end
355
+
356
+ def take
357
+ return self if has? 'cost'
358
+
359
+ Olib.wrap(action 'get') { |line|
360
+ raise Errors::DoesntExist if line=~ Olib::Dictionary.get[:failure][:ne]
361
+ raise Errors::HandsFull if line=~ Olib::Dictionary.get[:failure][:hands_full]
362
+ raise Errors::TooHeavy if line=~ Olib::Dictionary.get[:failure][:weight]
363
+
364
+ if line=~ Olib::Dictionary.get[:success]
365
+ raise Olib::Errors::Mundane
366
+ end
367
+
368
+ if line =~ Olib::Dictionary.get[:failure][:buy]
369
+ define 'cost', line.match(Olib::Dictionary.get[:failure][:buy])[:cost].to_i
370
+ raise Olib::Errors::Mundane
371
+ end
372
+
373
+ if line =~ /let me help you with that/
374
+ raise Olib::Errors::Mundane
375
+ end
376
+
377
+ if line=~ /You'll have to buy it if you want it/
378
+ tag 'buyable'
379
+ raise Olib::Errors::Mundane
380
+ end
381
+
382
+ if line=~ /You can PULL/
383
+ tag 'pullable'
384
+ raise Olib::Errors::Mundane
385
+ end
386
+
387
+ }
388
+ self
389
+ end
390
+
391
+ def drop
392
+ Script.log("#{Time.now} > dropped #{to_s}")
393
+ fput action "drop"
394
+ self
395
+ end
396
+
397
+ def _inspect
398
+
399
+ return self if has? 'inspect'
400
+
401
+ in_inspect = false
402
+
403
+ Olib.wrap_stream(action 'inspect') { |line|
404
+
405
+ raise Olib::Errors::Mundane if line =~ /^<prompt/ and in_inspect
406
+
407
+ # skip first inspect line because it's useless for info
408
+ if line =~ /You carefully inspect|You carefully count|goat/
409
+ in_inspect = true
410
+ end
411
+
412
+
413
+ if in_inspect
414
+
415
+ if line =~ /^You estimate that (?:.*?) can store (?:a|an|some) ([a-zA-Z -]+) amount with enough space for ([a-zA-Z ]+)/
416
+ @props['space'] = $1
417
+ @props['number_of_items'] = $2
418
+ end
419
+
420
+
421
+
422
+ if line =~ /^You determine that you could wear the (.*?) ([a-zA-Z ]+)/
423
+ @props['location']= $2
424
+ end
425
+
426
+ if line =~ /allows you to conclude that it is ([a-zA-Z ]+)/
427
+
428
+ if line =~ Dictionary.size
429
+ @props['shield_type'] = $1
430
+ else
431
+ Dictionary.armors.each do |type, re| @props['armor_type'] = type if line =~ re end
432
+ end
433
+
434
+ end
435
+
436
+ if line =~ /suitable for use in unarmed combat/
437
+ @props['weapon_type']= "uac"
438
+ end
439
+
440
+ if line =~ /requires skill in ([a-zA-Z ]+) to use effectively/
441
+
442
+ @props['weapon_type']= $1
443
+ if line =~ /It appears to be a modified ([a-zA-Z -]+)/
444
+ @props['weapon_base']= $1
445
+ else
446
+ @props['weapon_base']= @noun
447
+ end
448
+ end
449
+
450
+ if line =~ /^It looks like this item has been mainly crafted out of ([a-zA-Z -]+)./
451
+ @props['material']= $1
452
+ raise Olib::Errors::Mundane
453
+ end
454
+
455
+ if line =~ /can hold liquids/
456
+ @props['liquid_container']=true
457
+ end
458
+
459
+ end
460
+
461
+ }
462
+
463
+ return self
464
+ end
465
+
466
+ def look
467
+ return self if has? 'show'
468
+ Olib.wrap(action 'look') { |line|
469
+ raise Olib::Errors::Mundane if line=~/^You see nothing unusual.|^You can't quite get a good look at/
470
+ define 'show', line unless line=~/prompt time|You take a closer look/
471
+ }
472
+ self
473
+ end
474
+
475
+ def tap
476
+ return self if has? 'description'
477
+ Olib.wrap(action 'tap') { |line|
478
+ next unless line=~ /You tap (.*?) (on|in)/
479
+ define 'description', $1
480
+ raise Olib::Errors::Mundane
481
+ }
482
+ self
483
+ end
484
+
485
+ def price
486
+ return self if(has? 'price' or has? 'info')
487
+ Olib.wrap(action 'get') { |line|
488
+
489
+ if line =~ /(\d+) silvers/
490
+ define 'price', line.match(/(?<price>\d+) silvers/)[:price]
491
+ raise Olib::Errors::Mundane
492
+ end
493
+
494
+ if line =~ /You can't pick that up/
495
+ define "info", true
496
+ raise Olib::Errors::Mundane
497
+ end
498
+
499
+ Script.log "unmatched price: #{line}"
500
+
501
+ }
502
+ self
503
+ end
504
+
505
+ def read
506
+ return self if has? 'read'
507
+ scroll = false
508
+ multiline = false
509
+ Olib.wrap_stream(action 'read') { |line|
510
+
511
+ raise Olib::Errors::Mundane if line =~ /^<prompt/ and (multiline or scroll)
512
+ raise Olib::Errors::Mundane if line =~ /There is nothing there to read|You can't do that./
513
+
514
+ # if we are in a multiline state
515
+ @props['read'] = @props['read'].concat line if multiline
516
+
517
+ # capture spell
518
+ if scroll && line =~ /\(([0-9]+)\) ([a-zA-Z'\s]+)/
519
+ n = $1
520
+ name = $2
521
+ spell = {'n' => $1, 'name' => $2}
522
+ #Client.notify "Spell detected ... (#{$1}) #{$2}"
523
+ @props['spells'].push spell
524
+
525
+ # begin scroll
526
+ elsif line =~ /It takes you a moment to focus on the/
527
+ scroll = true
528
+ @props['spells'] = Array.new
529
+
530
+ # open multiline
531
+ elsif line =~ /^In the (.*?) language, it reads/
532
+ multiline = true
533
+ @props['read'] = "#{line}\n"
534
+ @props['language'] = $1
535
+
536
+ # alert to unknown
537
+ elsif line =~ /but the language is not one you know. It looks like it's written in (.*?)./
538
+ Script.log "Please find a friend that can read for #{$1} in #{XMLData.room_title}"
539
+ echo "Please find a friend that can read for #{$1} in #{XMLData.room_title}"
540
+ raise Olib::Errors::Mundane
541
+
542
+ end
543
+
544
+ }
545
+ return self
546
+ end
547
+
548
+ end
549
+
550
550
  end